feat: 升级公共组件

This commit is contained in:
Fu Diwei
2024-07-13 21:56:04 +08:00
parent 107fd3c206
commit 4acd2895a8
51 changed files with 146 additions and 508 deletions

View File

@@ -9,6 +9,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.Ads
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatAdsClientExecuteImagesExtensions
@@ -32,7 +33,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Ads
request.FileHash = EncodedString.ToHexString(Utilities.MD5Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Value!;
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
using var fileContent = new ByteArrayContent(request.FileBytes ?? Array.Empty<byte>());

View File

@@ -36,11 +36,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -4,6 +4,7 @@ using System.Xml.Linq;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
using SKIT.FlurlHttpClient.Internal;
using SKIT.FlurlHttpClient.Primitives;
/// <summary>
@@ -136,7 +137,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
try
{
xml = Utilities.XmlHelper.Serialize(webhookEvent);
xml = _XmlSimpleSerializer.Serialize(webhookEvent, typeof(TEvent));
}
catch (Exception ex)
{
@@ -367,7 +368,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
webhookXml = Utilities.WxMsgCryptor.AESDecrypt(cipherText: encryptedXml, encodingAESKey: client.Credentials.PushEncodingAESKey!, out _);
}
return Utilities.XmlHelper.Deserialize<TEvent>(webhookXml);
return (TEvent)_XmlSimpleSerializer.Deserialize(webhookXml, typeof(TEvent));
}
catch (WechatApiException)
{

View File

@@ -401,7 +401,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "card", "invoice", "platform", "setpdf")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "invoice.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", formDataName: "pdf");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "invoice.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", formDataName: "pdf");
return await client.SendFlurlRequestAsync<Models.CardInvoicePlatformSetPdfResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -430,7 +430,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "component", "uploadprivacyextfile")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "file");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "file");
return await client.SendFlurlRequestAsync<Models.CgibinComponentUploadPrivacyExtraFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -8,6 +8,8 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
using SKIT.FlurlHttpClient;
public static class WechatApiClientExecuteCgibinMaterialExtensions
{
/// <summary>
@@ -47,23 +49,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type) || TYPE_THUMB.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VOICE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVoice(request.FileName!) ?? "audio/mp3";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "material", "add_material")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("type", request.Type);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
if (TYPE_VIDEO.Equals(request.Type))
{
httpContent.Add(new StringContent(client.JsonSerializer.Serialize(request), Encoding.UTF8), "\"description\"");

View File

@@ -8,6 +8,8 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
using SKIT.FlurlHttpClient;
public static class WechatApiClientExecuteCgibinMediaExtensions
{
/// <summary>
@@ -48,23 +50,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type) || TYPE_THUMB.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VOICE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVoice(request.FileName!) ?? "audio/mp3";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "media", "upload")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("type", request.Type);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMediaUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -116,13 +109,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "media", "uploadimg")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMediaUploadImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -280,7 +280,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.SetQueryParam("height", request.Height)
.SetQueryParam("width", request.Width);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.ChannelsECBasicsImageUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
@@ -328,13 +328,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "channels", "ec", "basics", "qualification", "upload")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.ChannelsECBasicsQualificationUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -128,7 +128,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("kf_account", request.KfAccount);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.jpg", fileBytes: request.HeadImageFileBytes, fileContentType: "image/jpeg", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.jpg", fileBytes: request.HeadImageFileBytes, fileContentType: "image/jpeg", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CustomServiceKfAccountUploadHeadImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -44,7 +44,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.SetQueryParam("height", request.Height)
.SetQueryParam("width", request.Width);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.ProductImageUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -41,7 +41,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
{
flurlReq.SetQueryParam("upload_type", 0);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.png", fileBytes: request.ImageFileBytes!, fileContentType: "image/png", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.ShopImageUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -8,6 +8,8 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.Api
{
using SKIT.FlurlHttpClient;
public static class WechatApiClientExecuteWxaComponentExtensions
{
#region AMS
@@ -1120,23 +1122,16 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
if (request is null) throw new ArgumentNullException(nameof(request));
if (request.FileName is null)
{
request.FileName = Guid.NewGuid().ToString("N").ToLower();
}
if (request.FileContentType is null)
{
request.FileContentType =
Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ??
Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ??
"application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "uploadmedia")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.WxaUploadMediaResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
#endregion
@@ -1428,20 +1423,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "icp", "upload_icp_media")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
httpContent.Add(new StringContent(request.Type, Encoding.UTF8), "\"type\"");
httpContent.Add(new StringContent(request.ICPOrderField, Encoding.UTF8), "\"icp_order_field\"");
if (request.CertificateType is not null) httpContent.Add(new StringContent(request.CertificateType.ToString()!, Encoding.UTF8), "\"certificate_type\"");

View File

@@ -668,7 +668,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "imagesearch")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.ImageFileBytes, fileContentType: "image/png", formDataName: "img");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.png", fileBytes: request.ImageFileBytes, fileContentType: "image/png", formDataName: "img");
return await client.SendFlurlRequestAsync<Models.WxaImageSearchResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -741,7 +741,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "img_sec_check")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "image.png", fileBytes: request.FileBytes, fileContentType: "image/png", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "image.png", fileBytes: request.FileBytes, fileContentType: "image/png", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.WxaImageSecurityCheckResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -99,7 +99,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "sec", "uploadauthmaterial")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "media.png", fileBytes: request.FileBytes, fileContentType: "image/png", formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "media.png", fileBytes: request.FileBytes, fileContentType: "image/png", formDataName: "media");
return await client.SendFlurlRequestAsync<Models.WxaSecUploadAuthMaterialResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -435,7 +435,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "sec", "vod", "singlefileupload")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: $"{request.MediaName}.{request.MediaType}", fileBytes: request.MediaFileBytes, fileContentType: "video/mp4", formDataName: "media_data");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: $"{request.MediaName}.{request.MediaType}", fileBytes: request.MediaFileBytes, fileContentType: "video/mp4", formDataName: "media_data");
if (request.CoverType is not null) httpContent.Add(new StringContent(request.CoverType), "cover_type");
if (request.CoverFileBytes is not null) httpContent.Add(new ByteArrayContent(request.CoverFileBytes), "cover_data");
if (request.SourceContext is not null) httpContent.Add(new StringContent(request.SourceContext), "source_context");
@@ -531,7 +531,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Api
.CreateFlurlRequest(request, HttpMethod.Post, "wxa", "sec", "vod", "uploadpart")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: string.Empty, fileBytes: request.FileBytes, fileContentType: "application/octet-stream", formDataName: "data");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: string.Empty, fileBytes: request.FileBytes, fileContentType: "application/octet-stream", formDataName: "data");
httpContent.Add(new StringContent(request.UploadId), "upload_id");
httpContent.Add(new StringContent(request.PartNumber.ToString()), "part_number");
httpContent.Add(new StringContent(request.FileType.ToString()), "resource_type");

View File

@@ -35,13 +35,9 @@
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -1,50 +0,0 @@
using System.IO;
namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities
{
internal static class FileNameToContentTypeMapper
{
public static string? GetContentTypeForImage(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".jpg":
case ".jpeg":
return "image/jpeg";
case ".bmp":
return "image/bmp";
case ".png":
return "image/png";
}
return null;
}
public static string? GetContentTypeForVoice(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".mp3":
return "audio/mpeg";
case ".amr":
return "audio/amr";
}
return null;
}
public static string? GetContentTypeForVideo(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".mp4":
return "video/mp4";
}
return null;
}
}
}

View File

@@ -5,22 +5,18 @@ using System.Text;
namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities
{
internal static class FileHttpContentBuilder
{
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName)
{
return Build(fileName: fileName, fileBytes: fileBytes, fileContentType: fileContentType, formDataName: formDataName, (_) => { });
}
using SKIT.FlurlHttpClient;
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName, Action<HttpContent> configureFileHttpContent)
internal static class HttpContentBuilder
{
public static MultipartFormDataContent BuildWithFile(string fileName, byte[] fileBytes, string? fileContentType, string formDataName, Action<HttpContent>? configureFileHttpContent = null)
{
if (fileName is null) throw new ArgumentNullException(nameof(fileName));
if (formDataName is null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent is null) throw new ArgumentNullException(nameof(configureFileHttpContent));
fileName = fileName.Replace("\"", string.Empty);
fileBytes = fileBytes ?? Array.Empty<byte>();
fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType;
fileContentType = string.IsNullOrEmpty(fileContentType) ? MimeTypes.Binary : fileContentType;
formDataName = formDataName.Replace("\"", string.Empty);
// HACKED: 默认不支持 Unicode 文件名 https://github.com/dotnet/runtime/issues/22996
@@ -33,11 +29,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities
fileContent.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=\"{formDataName}\"; filename=\"{hackedFileName}\"");
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType);
fileContent.Headers.ContentLength = fileBytes.Length;
configureFileHttpContent(fileContent);
configureFileHttpContent?.Invoke(fileContent);
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
MultipartFormDataContent httpContent = new MultipartFormDataContent(boundary);
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}");
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"{MimeTypes.FormData}; boundary={boundary}");
httpContent.Add(fileContent);
return httpContent;
}

View File

@@ -1,31 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.Api.Utilities
{
using SKIT.FlurlHttpClient.Internal;
internal static class XmlHelper
{
public static string Serialize(object obj, Type type)
{
return _XmlSimpleSerializer.Serialize(obj, type);
}
public static string Serialize<T>(T obj)
where T : class
{
return Serialize(obj, typeof(T));
}
public static object Deserialize(string xml, Type type)
{
return _XmlSimpleSerializer.Deserialize(xml, type);
}
public static T Deserialize<T>(string xml)
where T : class
{
return (T)Deserialize(xml, typeof(T));
}
}
}

View File

@@ -36,11 +36,7 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayBusinessClientExecuteFileUploadsExtensions
@@ -33,12 +34,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness
request.FileHash = EncodedString.ToHexString(Utilities.SM3Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "mse-pay", "file-uploads");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462; net471; netstandard2.0; net6.0</TargetFrameworks>
<TargetFrameworks>net462; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
@@ -35,14 +35,10 @@
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462' Or '$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -1,24 +0,0 @@
using System.IO;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
{
internal static class FileNameToContentTypeMapper
{
public static string? GetContentTypeForImage(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".jpg":
case ".jpeg":
return "image/jpeg";
case ".bmp":
return "image/bmp";
case ".png":
return "image/png";
}
return null;
}
}
}

View File

@@ -1,40 +1,34 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
namespace SKIT.FlurlHttpClient.Wechat.TenpayBusiness.Utilities
{
internal static class FileHttpContentBuilder
{
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName = "file")
{
return Build(fileName: fileName, fileBytes: fileBytes, fileContentType: fileContentType, fileMetaJson: fileMetaJson, formDataName: formDataName, (_) => { }, (_) => { });
}
using SKIT.FlurlHttpClient;
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName, Action<HttpContent> configureMetaHttpContent, Action<HttpContent> configureFileHttpContent)
internal static class HttpContentBuilder
{
public static MultipartFormDataContent BuildWithFile(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName = "file", Action<HttpContent>? configureMetaHttpContent = null, Action<HttpContent>? configureFileHttpContent = null)
{
if (fileName is null) throw new ArgumentNullException(nameof(fileName));
if (fileMetaJson is null) throw new ArgumentNullException(nameof(fileMetaJson));
if (formDataName is null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent is null) throw new ArgumentNullException(nameof(configureFileHttpContent));
fileBytes = fileBytes ?? Array.Empty<byte>();
fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType;
fileContentType = string.IsNullOrEmpty(fileContentType) ? MimeTypes.Binary : fileContentType;
formDataName = formDataName.Replace("\"", string.Empty);
ByteArrayContent metaContent = new ByteArrayContent(Encoding.UTF8.GetBytes(fileMetaJson));
metaContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
configureMetaHttpContent(metaContent);
StringContent metaContent = new StringContent(fileMetaJson, null, MimeTypes.Json);
configureMetaHttpContent?.Invoke(metaContent);
ByteArrayContent fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType);
configureFileHttpContent(fileContent);
configureFileHttpContent?.Invoke(fileContent);
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
MultipartFormDataContent httpContent = new MultipartFormDataContent(boundary);
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}");
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"{MimeTypes.FormData}; boundary={boundary}");
httpContent.Add(metaContent, $"\"{Constants.FormDataFields.FORMDATA_META}\"");
httpContent.Add(fileContent, $"\"{formDataName}\"", $"\"{HttpUtility.UrlEncode(fileName)}\"");
return httpContent;

View File

@@ -36,8 +36,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2
string xml = xmlDocument.InnerXml;
string json = Utilities.XmlHelper.ConvertToJson(xml);
string signData = Utilities.JsonHelper.ParseToSortedQueryString(json);
string actualSign = Utilities.RequestSigner.SignFromSortedQueryString(signData, client.Credentials.MerchantSecret, signType);
string actualSign = Utilities.RequestSigner.SignFromJson(json, client.Credentials.MerchantSecret, signType);
bool valid = string.Equals(expectedSign, actualSign, StringComparison.OrdinalIgnoreCase);
if (valid)

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461; net471; netstandard2.0; net6.0</TargetFrameworks>
<TargetFrameworks>net462; net471; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
@@ -36,12 +36,12 @@
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Http.WebRequest" Condition="'$(TargetFramework)' == 'net461' Or '$(TargetFramework)' == 'net471'" />
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net461' Or '$(TargetFramework)' == 'net471'" />
<Reference Include="System.Net.Http.WebRequest" Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
<PackageReference Include="System.Net.Http.WinHttpHandler" Version="8.0.1" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
</ItemGroup>
</Project>

View File

@@ -13,16 +13,22 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Settings
#if NETCOREAPP2_1_OR_GREATER || NET5_0_OR_GREATER
SocketsHttpHandler handler = new SocketsHttpHandler();
handler.SslOptions = new SslClientAuthenticationOptions() { RemoteCertificateValidationCallback = static (_, _, _, _) => true };
#else
#elif NET471_OR_GREATER
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = static (_, _, _, sslPolicyErrors) => sslPolicyErrors == SslPolicyErrors.None;
#elif NET462_OR_GREATER
WebRequestHandler handler = new WebRequestHandler();
handler.ServerCertificateValidationCallback = static (_, _, _, _) => true;
#else
WinHttpHandler handler = new WinHttpHandler();
handler.ServerCertificateValidationCallback = static (_, _, _, _) => true;
#endif
if (certificateBytes is not null)
{
X509Certificate x509;
#if NET471_OR_GREATER || NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
#if NET471_OR_GREATER || NETCOREAPP2_1_OR_GREATER || NET5_0_OR_GREATER
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
#else
if (Environment.OSVersion.Platform == PlatformID.Win32NT)

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
{
internal static class JsonHelper
{
internal static string ParseToSortedQueryString(string json)
{
if (string.IsNullOrEmpty(json))
return string.Empty;
StringBuilder stringBuilder = new StringBuilder();
JsonObject jObject = JsonNode.Parse(json)!.AsObject();
foreach (KeyValuePair<string, JsonNode?> jProp in jObject.OrderBy(p => p.Key))
{
string key = jProp.Key;
string? value = jProp.Value?.ToString();
if (string.IsNullOrEmpty(value))
continue;
stringBuilder.Append($"{key}={value}&");
}
return stringBuilder.ToString().TrimEnd('&');
}
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Nodes;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
{
@@ -29,7 +31,20 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV2.Utilities
public static string SignFromJson(string json, string secretKey, string secretValue, string? signType = null)
{
return SignFromSortedQueryString(queryString: JsonHelper.ParseToSortedQueryString(json), secretKey: secretKey, secretValue: secretValue, signType: signType);
StringBuilder stringBuilder = new StringBuilder();
JsonObject jObject = JsonNode.Parse(json)!.AsObject();
foreach (KeyValuePair<string, JsonNode?> jProp in jObject.OrderBy(p => p.Key))
{
string key = jProp.Key;
string? value = jProp.Value?.ToString();
if (string.IsNullOrEmpty(value))
continue;
stringBuilder.Append($"{key}={value}&");
}
string sortedQueryString = stringBuilder.ToString().TrimEnd('&');
return SignFromSortedQueryString(queryString: sortedQueryString, secretKey: secretKey, secretValue: secretValue, signType: signType);
}
public static string SignFromSortedQueryString(string queryString, string secret, string? signType = null)

View File

@@ -3,10 +3,12 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Flurl.Http;
using SKIT.FlurlHttpClient.Primitives;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayGlobalClientExecuteMerchantsExtensions
{
/// <summary>
@@ -110,12 +112,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/jpeg";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "merchant", "media", "upload");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMerchantMediaImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -5,11 +5,5 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global.Models
/// </summary>
public class UpdateMerchantH5PermissionApplicationResponse : GetMerchantH5PermissionApplicationByApplymentIdResponse
{
/// <summary>
/// 获取或设置申请单 ID。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string ApplymentId { get; set; } = string.Empty;
}
}

View File

@@ -5,11 +5,5 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.ExtendedSDK.Global.Models
/// </summary>
public class UpdateMerchantH5PermissionDomainApplicationResponse : GetMerchantH5PermissionDomainApplicationByApplymentIdResponse
{
/// <summary>
/// 获取或设置申请单 ID。
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public string ApplymentId { get; set; } = string.Empty;
}
}

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteEcommerceExtensions
@@ -78,12 +79,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "ecommerce", "account", "cancel-applications", "media");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadEcommerceAccountCancelApplicationMediaResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
#endregion

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteMarketingBankExtensions
@@ -34,12 +35,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes ?? Array.Empty<byte>())).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "text/plain";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "marketing", "bank", "packages", request.PackageId, "tasks");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes!, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes!, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMarketingBankPackagesTasksResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteMarketingMediaExtensions
@@ -38,12 +39,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "marketing", "favor", "media", "image-upload");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMarketingMediaImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteMarketingShoppingReceiptExtensions
@@ -33,12 +34,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "marketing", "shopping-receipt", "shoppingreceipts");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMarketingShoppingReceiptResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -6,6 +6,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteMerchantMediaExtensions
@@ -35,12 +36,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "merchant", "media", "upload");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMerchantMediaImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -67,12 +68,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "merchant", "media", "video_upload");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMerchantMediaVideoResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
}

View File

@@ -7,6 +7,7 @@ using Flurl.Http;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Primitives;
public static class WechatTenpayClientExecuteMerchantServiceExtensions
@@ -288,12 +289,12 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
request.FileHash = EncodedString.ToHexString(Utilities.SHA256Utility.Hash(request.FileBytes)).Value!.ToLower();
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "merchant-service", "images", "upload");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadMerchantServiceImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -397,7 +397,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "new-tax-control-fapiao", "fapiao-applications", "upload-fapiao-file");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "file.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "file.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadNewTaxControlFapiaoApplicationFapiaoFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -222,7 +222,7 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "taxi-invoice", "cards", "upload-file");
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: "file.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", fileMetaJson: client.JsonSerializer.Serialize(request));
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: "file.pdf", fileBytes: request.FileBytes, fileContentType: "application/pdf", fileMetaJson: client.JsonSerializer.Serialize(request));
return await client.SendFlurlRequestAsync<Models.UploadTaxiInvoiceCardFileResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -18,19 +18,5 @@ namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Models
[Newtonsoft.Json.JsonProperty("user_label")]
[System.Text.Json.Serialization.JsonPropertyName("user_label")]
public string? UserLabel { get; set; }
/// <summary>
/// 获取或设置用户分层。
/// </summary>
[Newtonsoft.Json.JsonProperty("user_risk_level")]
[System.Text.Json.Serialization.JsonPropertyName("user_risk_level")]
public int? UserRiskLevel { get; set; }
/// <summary>
/// 获取或设置分层版本。
/// </summary>
[Newtonsoft.Json.JsonProperty("risk_level_version")]
[System.Text.Json.Serialization.JsonPropertyName("risk_level_version")]
public int? RiskLevelVersion { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462; net471; netstandard2.0; net6.0</TargetFrameworks>
<TargetFrameworks>net462; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
@@ -35,14 +35,10 @@
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462' Or '$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net462'" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -1,54 +0,0 @@
using System.IO;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
internal static class FileNameToContentTypeMapper
{
public static string? GetContentTypeForImage(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".jpg":
case ".jpeg":
return "image/jpeg";
case ".bmp":
return "image/bmp";
case ".png":
return "image/png";
}
return null;
}
public static string? GetContentTypeForVideo(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".avi":
return "video/x-msvideo";
case ".wmv":
return "video/x-ms-wmv";
case ".mpeg":
return "video/mpeg";
case ".mov":
return "video/quicktime";
case ".mkv":
return "video/mkv";
case ".m4v":
return "video/x-m4v";
case ".flv":
return "video/x-flv";
case ".f4v":
return "video/x-f4v";
case ".rmvb":
return "video/vnd.rn-realvideo";
case ".mp4":
return "video/mp4";
}
return null;
}
}
}

View File

@@ -1,42 +1,35 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Web;
namespace SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities
{
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Wechat.TenpayV3.Constants;
internal static class FileHttpContentBuilder
internal static class HttpContentBuilder
{
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName = "file")
{
return Build(fileName: fileName, fileBytes: fileBytes, fileContentType: fileContentType, fileMetaJson: fileMetaJson, formDataName: formDataName, (_) => { }, (_) => { });
}
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string fileMetaJson, string formDataName, Action<HttpContent> configureMetaHttpContent, Action<HttpContent> configureFileHttpContent)
public static MultipartFormDataContent BuildWithFile(string fileName, byte[] fileBytes, string? fileContentType, string fileMetaJson, string formDataName = "file", Action<HttpContent>? configureMetaHttpContent = null, Action<HttpContent>? configureFileHttpContent = null)
{
if (fileName is null) throw new ArgumentNullException(nameof(fileName));
if (fileMetaJson is null) throw new ArgumentNullException(nameof(fileMetaJson));
if (formDataName is null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent is null) throw new ArgumentNullException(nameof(configureFileHttpContent));
fileBytes = fileBytes ?? Array.Empty<byte>();
fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType;
fileContentType = string.IsNullOrEmpty(fileContentType) ? MimeTypes.Binary : fileContentType;
formDataName = formDataName.Replace("\"", string.Empty);
ByteArrayContent metaContent = new ByteArrayContent(Encoding.UTF8.GetBytes(fileMetaJson));
metaContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
configureMetaHttpContent(metaContent);
StringContent metaContent = new StringContent(fileMetaJson, null, MimeTypes.Json);
configureMetaHttpContent?.Invoke(metaContent);
ByteArrayContent fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType);
configureFileHttpContent(fileContent);
configureFileHttpContent?.Invoke(fileContent);
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
MultipartFormDataContent httpContent = new MultipartFormDataContent(boundary);
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}");
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"{MimeTypes.FormData}; boundary={boundary}");
httpContent.Add(metaContent, $"\"{FormDataFields.FORMDATA_META}\"");
httpContent.Add(fileContent, $"\"{formDataName}\"", $"\"{HttpUtility.UrlEncode(fileName)}\"");
return httpContent;

View File

@@ -3,6 +3,7 @@ using System.Xml.Linq;
namespace SKIT.FlurlHttpClient.Wechat.Work
{
using SKIT.FlurlHttpClient.Internal;
using SKIT.FlurlHttpClient.Primitives;
/// <summary>
@@ -130,7 +131,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
try
{
xml = Utilities.XmlHelper.Serialize(webhookEvent);
xml = _XmlSimpleSerializer.Serialize(webhookEvent, typeof(TEvent));
}
catch (Exception ex)
{
@@ -363,7 +364,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
throw new WechatWorkException("Failed to decrypt event data, because the encrypted data is empty.");
webhookXml = Utilities.WxMsgCryptor.AESDecrypt(cipherText: encryptedXml!, encodingAESKey: client.Credentials.PushEncodingAESKey!, out _);
return Utilities.XmlHelper.Deserialize<TEvent>(webhookXml);
return (TEvent)_XmlSimpleSerializer.Deserialize(webhookXml, typeof(TEvent));
}
catch (WechatWorkException)
{

View File

@@ -45,23 +45,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VOICE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVoice(request.FileName!) ?? "audio/mp3";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "media", "upload")
.SetQueryParam("access_token", request.AccessToken)
.SetQueryParam("type", request.Type);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMediaUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -87,13 +78,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "media", "uploadimg")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMediaUploadImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -132,16 +123,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else if (TYPE_FILE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVoice(request.FileName!) ?? "text/plain";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "media", "upload_attachment")
@@ -149,7 +131,7 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
.SetQueryParam("media_type", request.Type)
.SetQueryParam("attachment_type", request.AttachmentType);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMediaUploadAttachmentResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -29,13 +29,13 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
request.FileName = Guid.NewGuid().ToString("N").ToLower() + ".png";
if (request.FileContentType is null)
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "miniapppay", "upload_image")
.SetQueryParam("access_token", request.AccessToken);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinMiniAppPayUploadImageResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}

View File

@@ -341,23 +341,14 @@ namespace SKIT.FlurlHttpClient.Wechat.Work
}
if (request.FileContentType is null)
{
if (TYPE_IMAGE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForImage(request.FileName!) ?? "image/png";
else if (TYPE_VOICE.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVoice(request.FileName!) ?? "audio/mp3";
else if (TYPE_VIDEO.Equals(request.Type))
request.FileContentType = Utilities.FileNameToContentTypeMapper.GetContentTypeForVideo(request.FileName!) ?? "video/mp4";
else
request.FileContentType = "application/octet-stream";
}
request.FileContentType = MimeTypes.GetMimeMapping(request.FileName!);
IFlurlRequest flurlReq = client
.CreateFlurlRequest(request, HttpMethod.Post, "cgi-bin", "service", "media", "upload")
.SetQueryParam("provider_access_token", request.ProviderAccessToken)
.SetQueryParam("type", request.Type);
using var httpContent = Utilities.FileHttpContentBuilder.Build(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
using var httpContent = Utilities.HttpContentBuilder.BuildWithFile(fileName: request.FileName, fileBytes: request.FileBytes, fileContentType: request.FileContentType, formDataName: "media");
return await client.SendFlurlRequestAsync<Models.CgibinServiceMediaUploadResponse>(flurlReq, httpContent: httpContent, cancellationToken: cancellationToken).ConfigureAwait(false);
}
#endregion

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net462; net471; netstandard2.0; net6.0</TargetFrameworks>
<TargetFrameworks>net462; netstandard2.0; net6.0</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<NullableReferenceTypes>true</NullableReferenceTypes>
@@ -42,13 +42,9 @@
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Web" Condition="'$(TargetFramework)' == 'net462' Or '$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.0.0" />
<PackageReference Include="SKIT.FlurlHttpClient.Common" Version="3.1.0" />
</ItemGroup>
</Project>

View File

@@ -1,50 +0,0 @@
using System.IO;
namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
{
internal static class FileNameToContentTypeMapper
{
public static string? GetContentTypeForImage(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".jpg":
case ".jpeg":
return "image/jpeg";
case ".gif":
return "image/gif";
case ".png":
return "image/bmp";
}
return null;
}
public static string? GetContentTypeForVoice(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".mp3":
return "audio/mpeg";
case ".amr":
return "audio/amr";
}
return null;
}
public static string? GetContentTypeForVideo(string fileName)
{
string extension = Path.GetExtension(fileName ?? "/")?.ToLower() ?? string.Empty;
switch (extension)
{
case ".mp4":
return "video/mp4";
}
return null;
}
}
}

View File

@@ -5,22 +5,18 @@ using System.Text;
namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
{
internal static class FileHttpContentBuilder
{
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName)
{
return Build(fileName: fileName, fileBytes: fileBytes, fileContentType: fileContentType, formDataName: formDataName, (_) => { });
}
using SKIT.FlurlHttpClient;
public static MultipartFormDataContent Build(string fileName, byte[] fileBytes, string fileContentType, string formDataName, Action<HttpContent> configureFileHttpContent)
internal static class HttpContentBuilder
{
public static MultipartFormDataContent BuildWithFile(string fileName, byte[] fileBytes, string? fileContentType, string formDataName, Action<HttpContent>? configureFileHttpContent = null)
{
if (fileName is null) throw new ArgumentNullException(nameof(fileName));
if (formDataName is null) throw new ArgumentNullException(nameof(formDataName));
if (configureFileHttpContent is null) throw new ArgumentNullException(nameof(configureFileHttpContent));
fileName = fileName.Replace("\"", string.Empty);
fileBytes = fileBytes ?? Array.Empty<byte>();
fileContentType = string.IsNullOrEmpty(fileContentType) ? "application/octet-stream" : fileContentType;
fileContentType = string.IsNullOrEmpty(fileContentType) ? MimeTypes.Binary : fileContentType;
formDataName = formDataName.Replace("\"", string.Empty);
// HACKED: 默认不支持 Unicode 文件名 https://github.com/dotnet/runtime/issues/22996
@@ -33,11 +29,11 @@ namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
fileContent.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=\"{formDataName}\"; filename=\"{hackedFileName}\"");
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(fileContentType);
fileContent.Headers.ContentLength = fileBytes.Length;
configureFileHttpContent(fileContent);
configureFileHttpContent?.Invoke(fileContent);
string boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x");
MultipartFormDataContent httpContent = new MultipartFormDataContent(boundary);
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}");
httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"{MimeTypes.FormData}; boundary={boundary}");
httpContent.Add(fileContent);
return httpContent;
}

View File

@@ -1,31 +0,0 @@
using System;
namespace SKIT.FlurlHttpClient.Wechat.Work.Utilities
{
using SKIT.FlurlHttpClient.Internal;
internal static class XmlHelper
{
public static string Serialize(object obj, Type type)
{
return _XmlSimpleSerializer.Serialize(obj, type);
}
public static string Serialize<T>(T obj)
where T : class
{
return Serialize(obj, typeof(T));
}
public static object Deserialize(string xml, Type type)
{
return _XmlSimpleSerializer.Deserialize(xml, type);
}
public static T Deserialize<T>(string xml)
where T : class
{
return (T)Deserialize(xml, typeof(T));
}
}
}