From ea7f0668766394b59b8d102b02efe3f305a5e62f Mon Sep 17 00:00:00 2001 From: MonkSoul Date: Mon, 5 Sep 2022 10:40:09 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=8A=20=E8=B0=83=E6=95=B4=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E8=AF=B7=E6=B1=82=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=EF=BC=88=E7=A0=B4=E5=9D=8F=E6=80=A7=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=EF=BC=89=20#I5PMS5=20#I5PIYI=20!553?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 支持远程请求代理模式动态设置一个或多个文件信息,如 HttpFile,HttpFile[],IList - 支持远程请求所有模式支持 HttpFile 或集合方式 - 调整 .SetBodyBytes 为 .SetFiles - 调整 .SetBodyBytes 参数签名为 params HttpFile[] files - 移除 [BodyBytes] 特性设计 - 更新远程请求文档 - 编写更新日志内容 --- .../RemoteRequestStringExtensions.cs | 8 +-- .../HttpFile.cs} | 53 ++++++++++++------ .../RemoteRequest/Internal/HttpRequestPart.cs | 4 +- .../Internal/HttpRequestPartMethods.cs | 16 +++--- .../Internal/HttpRequestPartSetters.cs | 9 ++-- .../Proxies/HttpDispatchProxy.cs | 35 +++++++++--- .../RemoteRequestStringExtensions.cs | 8 +-- .../HttpFile.cs} | 53 ++++++++++++------ .../RemoteRequest/Internal/HttpRequestPart.cs | 4 +- .../Internal/HttpRequestPartMethods.cs | 16 +++--- .../Internal/HttpRequestPartSetters.cs | 9 ++-- .../Proxies/HttpDispatchProxy.cs | 35 +++++++++--- handbook/docs/http.mdx | 54 +++++++++++++++++-- handbook/docs/upgrade.mdx | 31 ++++++++++- samples/Furion.Application/IHttp.cs | 4 +- .../Furion.Application/TestModuleServices.cs | 9 ++-- 16 files changed, 252 insertions(+), 96 deletions(-) rename framework/Furion.Pure/RemoteRequest/{Attributes/Parameters/BodyBytesAttribute.cs => Internal/HttpFile.cs} (58%) rename framework/Furion/RemoteRequest/{Attributes/Parameters/BodyBytesAttribute.cs => Internal/HttpFile.cs} (58%) diff --git a/framework/Furion.Pure/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs b/framework/Furion.Pure/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs index dbdd56359b..f403c4541f 100644 --- a/framework/Furion.Pure/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs +++ b/framework/Furion.Pure/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs @@ -166,14 +166,14 @@ public static class RemoteRequestStringExtensions } /// - /// 设置 Body Bytes + /// 设置文件 /// /// - /// + /// /// - public static HttpRequestPart SetBodyBytes(this string requestUrl, params (string Name, byte[] Bytes, string FileName)[] bytesData) + public static HttpRequestPart SetFiles(this string requestUrl, params HttpFile[] files) { - return HttpRequestPart.Default().SetRequestUrl(requestUrl).SetBodyBytes(bytesData); + return HttpRequestPart.Default().SetRequestUrl(requestUrl).SetFiles(files); } /// diff --git a/framework/Furion.Pure/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs b/framework/Furion.Pure/RemoteRequest/Internal/HttpFile.cs similarity index 58% rename from framework/Furion.Pure/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs rename to framework/Furion.Pure/RemoteRequest/Internal/HttpFile.cs index cc39bb57c5..e28b8d3df2 100644 --- a/framework/Furion.Pure/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs +++ b/framework/Furion.Pure/RemoteRequest/Internal/HttpFile.cs @@ -23,38 +23,59 @@ namespace Furion.RemoteRequest; /// -/// 配置 Body Bytes 参数 +/// 远程请求文件类 /// -[SuppressSniffer, AttributeUsage(AttributeTargets.Parameter)] -public class BodyBytesAttribute : ParameterBaseAttribute +[SuppressSniffer] +public sealed class HttpFile { /// - /// 构造函数 + /// 创建 HttpFile 类 /// - /// - public BodyBytesAttribute(string alias) + /// + /// + /// + /// + public static HttpFile Create(string name, byte[] bytes, string fileName = default) { - Alias = alias; + return new HttpFile + { + Name = name, + Bytes = bytes, + FileName = fileName + }; } /// - /// 构造函数 + /// 添加多个文件 /// - /// - /// - public BodyBytesAttribute(string alias, string fileName) + /// + /// + /// + public static HttpFile[] CreateMultiple(string name, params (byte[] bytes, string fileName)[] items) { - Alias = alias; - FileName = fileName; + var files = new List(); + if (items == null || items.Length == 0) return files.ToArray(); + + foreach (var (bytes, fileName) in items) + { + files.Add(Create(name, bytes, fileName)); + } + + return files.ToArray(); } /// - /// 参数别名 + /// 表单名 /// - public string Alias { get; set; } + public string Name { get; set; } /// /// 文件名 /// public string FileName { get; set; } -} \ No newline at end of file + + /// + /// 文件字节数组 + /// + public byte[] Bytes { get; set; } +} diff --git a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPart.cs b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPart.cs index fb5a83f1a8..37173e43dc 100644 --- a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPart.cs +++ b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPart.cs @@ -89,9 +89,9 @@ public sealed partial class HttpRequestPart public Encoding ContentEncoding { get; private set; } = Encoding.UTF8; /// - /// 设置 Body Bytes 类型 + /// 上传文件 /// - public List<(string Name, byte[] Bytes, string FileName)> BodyBytes { get; private set; } = new List<(string Name, byte[] Bytes, string FileName)>(); + public List Files { get; private set; } = new(); /// /// 超时时间(秒),默认 100 秒 diff --git a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartMethods.cs b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartMethods.cs index f2fff63642..b6da9efcc1 100644 --- a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartMethods.cs +++ b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartMethods.cs @@ -636,18 +636,18 @@ public sealed partial class HttpRequestPart var multipartFormDataContent = new MultipartFormDataContent(boundary); // 添加 Bytes 类型 - foreach (var (Name, Bytes, FileName) in BodyBytes) + foreach (var httpFile in Files) { // 获取文件 Content-Type 类型 - FS.TryGetContentType(FileName, out var contentType); + FS.TryGetContentType(httpFile.FileName, out var contentType); - var byteArrayContent = new ByteArrayContent(Bytes); + var byteArrayContent = new ByteArrayContent(httpFile.Bytes); byteArrayContent.Headers.TryAddWithoutValidation("Content-Type", contentType ?? "application/octet-stream"); - if (string.IsNullOrWhiteSpace(FileName)) - multipartFormDataContent.Add(byteArrayContent, $"\"{Name}\""); + if (string.IsNullOrWhiteSpace(httpFile.FileName)) + multipartFormDataContent.Add(byteArrayContent, $"\"{httpFile.Name}\""); else - multipartFormDataContent.Add(byteArrayContent, $"\"{Name}\"", $"\"{FileName}\""); + multipartFormDataContent.Add(byteArrayContent, $"\"{httpFile.Name}\"", $"\"{httpFile.FileName}\""); } // 处理其他类型 @@ -669,9 +669,9 @@ public sealed partial class HttpRequestPart break; case "application/octet-stream": - if (BodyBytes.Count > 0 && BodyBytes[0].Bytes.Length > 0) + if (Files.Count > 0 && Files[0].Bytes.Length > 0) { - httpContent = new ByteArrayContent(BodyBytes[0].Bytes); + httpContent = new ByteArrayContent(Files[0].Bytes); // 设置内容类型 httpContent.Headers.ContentType = new MediaTypeHeaderValue(ContentType); diff --git a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartSetters.cs b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartSetters.cs index d59d09eb9d..8ab926bfeb 100644 --- a/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartSetters.cs +++ b/framework/Furion.Pure/RemoteRequest/Internal/HttpRequestPartSetters.cs @@ -183,14 +183,13 @@ public sealed partial class HttpRequestPart } /// - /// 设置 Body Bytes + /// 设置文件 /// - /// + /// /// - public HttpRequestPart SetBodyBytes(params (string Name, byte[] Bytes, string FileName)[] bytesData) + public HttpRequestPart SetFiles(params HttpFile[] files) { - BodyBytes ??= new List<(string Name, byte[] Bytes, string FileName)>(); - if (bytesData != null && bytesData.Length > 0) BodyBytes.AddRange(bytesData); + Files.AddRange(files); return this; } diff --git a/framework/Furion.Pure/RemoteRequest/Proxies/HttpDispatchProxy.cs b/framework/Furion.Pure/RemoteRequest/Proxies/HttpDispatchProxy.cs index 6250accde0..a7fc1d5f56 100644 --- a/framework/Furion.Pure/RemoteRequest/Proxies/HttpDispatchProxy.cs +++ b/framework/Furion.Pure/RemoteRequest/Proxies/HttpDispatchProxy.cs @@ -197,19 +197,38 @@ public class HttpDispatchProxy : AspectDispatchProxy, IDispatchProxy httpRequestPart.SetBody(bodyParameter.Value, bodyAttribute.ContentType, Encoding.GetEncoding(bodyAttribute.Encoding)); } - // 查找所有贴了 [BodyBytes] 特性的参数 - var bodyBytesParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(BodyBytesAttribute), true)); - if (bodyBytesParameters != null) + // 查找所有 HttpFile 和 HttpFile 集合类型的参数 + var filesParameters = parameters.Where(u => u.Parameter.ParameterType == typeof(HttpFile) + || u.Parameter.ParameterType == typeof(HttpFile[]) + || (u.Parameter.ParameterType.HasImplementedRawGeneric(typeof(IEnumerable<>)) && u.Parameter.ParameterType.GenericTypeArguments[0] == typeof(HttpFile))); + + if (filesParameters != null) { - var bodyBytes = new List<(string Name, byte[] Bytes, string FileName)>(); + var files = new List(); - foreach (var item in bodyBytesParameters) + foreach (var item in filesParameters) { - var bodyBytesAttribute = item.Parameter.GetCustomAttribute(); - if (item.Value != null && item.Value.GetType() == typeof(byte[])) bodyBytes.Add((bodyBytesAttribute.Alias ?? item.Name, (byte[])item.Value, bodyBytesAttribute.FileName)); + if (item.Value != null) + { + // 处理 HttpFile[] 类型 + if (item.Parameter.ParameterType.IsArray) + { + files.AddRange((HttpFile[])item.Value); + } + // 处理 IList IEnumerable 类型 + else if (typeof(IEnumerable).IsAssignableFrom(item.Parameter.ParameterType)) + { + files.AddRange(((IEnumerable)item.Value).Cast()); + } + // 处理单个类型 + else + { + files.Add((HttpFile)item.Value); + } + } } - httpRequestPart.SetBodyBytes(bodyBytes.ToArray()); + httpRequestPart.SetFiles(files.ToArray()); } } diff --git a/framework/Furion/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs b/framework/Furion/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs index dbdd56359b..f403c4541f 100644 --- a/framework/Furion/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs +++ b/framework/Furion/RemoteRequest/Extensions/RemoteRequestStringExtensions.cs @@ -166,14 +166,14 @@ public static class RemoteRequestStringExtensions } /// - /// 设置 Body Bytes + /// 设置文件 /// /// - /// + /// /// - public static HttpRequestPart SetBodyBytes(this string requestUrl, params (string Name, byte[] Bytes, string FileName)[] bytesData) + public static HttpRequestPart SetFiles(this string requestUrl, params HttpFile[] files) { - return HttpRequestPart.Default().SetRequestUrl(requestUrl).SetBodyBytes(bytesData); + return HttpRequestPart.Default().SetRequestUrl(requestUrl).SetFiles(files); } /// diff --git a/framework/Furion/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs b/framework/Furion/RemoteRequest/Internal/HttpFile.cs similarity index 58% rename from framework/Furion/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs rename to framework/Furion/RemoteRequest/Internal/HttpFile.cs index cc39bb57c5..e28b8d3df2 100644 --- a/framework/Furion/RemoteRequest/Attributes/Parameters/BodyBytesAttribute.cs +++ b/framework/Furion/RemoteRequest/Internal/HttpFile.cs @@ -23,38 +23,59 @@ namespace Furion.RemoteRequest; /// -/// 配置 Body Bytes 参数 +/// 远程请求文件类 /// -[SuppressSniffer, AttributeUsage(AttributeTargets.Parameter)] -public class BodyBytesAttribute : ParameterBaseAttribute +[SuppressSniffer] +public sealed class HttpFile { /// - /// 构造函数 + /// 创建 HttpFile 类 /// - /// - public BodyBytesAttribute(string alias) + /// + /// + /// + /// + public static HttpFile Create(string name, byte[] bytes, string fileName = default) { - Alias = alias; + return new HttpFile + { + Name = name, + Bytes = bytes, + FileName = fileName + }; } /// - /// 构造函数 + /// 添加多个文件 /// - /// - /// - public BodyBytesAttribute(string alias, string fileName) + /// + /// + /// + public static HttpFile[] CreateMultiple(string name, params (byte[] bytes, string fileName)[] items) { - Alias = alias; - FileName = fileName; + var files = new List(); + if (items == null || items.Length == 0) return files.ToArray(); + + foreach (var (bytes, fileName) in items) + { + files.Add(Create(name, bytes, fileName)); + } + + return files.ToArray(); } /// - /// 参数别名 + /// 表单名 /// - public string Alias { get; set; } + public string Name { get; set; } /// /// 文件名 /// public string FileName { get; set; } -} \ No newline at end of file + + /// + /// 文件字节数组 + /// + public byte[] Bytes { get; set; } +} diff --git a/framework/Furion/RemoteRequest/Internal/HttpRequestPart.cs b/framework/Furion/RemoteRequest/Internal/HttpRequestPart.cs index fb5a83f1a8..37173e43dc 100644 --- a/framework/Furion/RemoteRequest/Internal/HttpRequestPart.cs +++ b/framework/Furion/RemoteRequest/Internal/HttpRequestPart.cs @@ -89,9 +89,9 @@ public sealed partial class HttpRequestPart public Encoding ContentEncoding { get; private set; } = Encoding.UTF8; /// - /// 设置 Body Bytes 类型 + /// 上传文件 /// - public List<(string Name, byte[] Bytes, string FileName)> BodyBytes { get; private set; } = new List<(string Name, byte[] Bytes, string FileName)>(); + public List Files { get; private set; } = new(); /// /// 超时时间(秒),默认 100 秒 diff --git a/framework/Furion/RemoteRequest/Internal/HttpRequestPartMethods.cs b/framework/Furion/RemoteRequest/Internal/HttpRequestPartMethods.cs index f2fff63642..b6da9efcc1 100644 --- a/framework/Furion/RemoteRequest/Internal/HttpRequestPartMethods.cs +++ b/framework/Furion/RemoteRequest/Internal/HttpRequestPartMethods.cs @@ -636,18 +636,18 @@ public sealed partial class HttpRequestPart var multipartFormDataContent = new MultipartFormDataContent(boundary); // 添加 Bytes 类型 - foreach (var (Name, Bytes, FileName) in BodyBytes) + foreach (var httpFile in Files) { // 获取文件 Content-Type 类型 - FS.TryGetContentType(FileName, out var contentType); + FS.TryGetContentType(httpFile.FileName, out var contentType); - var byteArrayContent = new ByteArrayContent(Bytes); + var byteArrayContent = new ByteArrayContent(httpFile.Bytes); byteArrayContent.Headers.TryAddWithoutValidation("Content-Type", contentType ?? "application/octet-stream"); - if (string.IsNullOrWhiteSpace(FileName)) - multipartFormDataContent.Add(byteArrayContent, $"\"{Name}\""); + if (string.IsNullOrWhiteSpace(httpFile.FileName)) + multipartFormDataContent.Add(byteArrayContent, $"\"{httpFile.Name}\""); else - multipartFormDataContent.Add(byteArrayContent, $"\"{Name}\"", $"\"{FileName}\""); + multipartFormDataContent.Add(byteArrayContent, $"\"{httpFile.Name}\"", $"\"{httpFile.FileName}\""); } // 处理其他类型 @@ -669,9 +669,9 @@ public sealed partial class HttpRequestPart break; case "application/octet-stream": - if (BodyBytes.Count > 0 && BodyBytes[0].Bytes.Length > 0) + if (Files.Count > 0 && Files[0].Bytes.Length > 0) { - httpContent = new ByteArrayContent(BodyBytes[0].Bytes); + httpContent = new ByteArrayContent(Files[0].Bytes); // 设置内容类型 httpContent.Headers.ContentType = new MediaTypeHeaderValue(ContentType); diff --git a/framework/Furion/RemoteRequest/Internal/HttpRequestPartSetters.cs b/framework/Furion/RemoteRequest/Internal/HttpRequestPartSetters.cs index d59d09eb9d..8ab926bfeb 100644 --- a/framework/Furion/RemoteRequest/Internal/HttpRequestPartSetters.cs +++ b/framework/Furion/RemoteRequest/Internal/HttpRequestPartSetters.cs @@ -183,14 +183,13 @@ public sealed partial class HttpRequestPart } /// - /// 设置 Body Bytes + /// 设置文件 /// - /// + /// /// - public HttpRequestPart SetBodyBytes(params (string Name, byte[] Bytes, string FileName)[] bytesData) + public HttpRequestPart SetFiles(params HttpFile[] files) { - BodyBytes ??= new List<(string Name, byte[] Bytes, string FileName)>(); - if (bytesData != null && bytesData.Length > 0) BodyBytes.AddRange(bytesData); + Files.AddRange(files); return this; } diff --git a/framework/Furion/RemoteRequest/Proxies/HttpDispatchProxy.cs b/framework/Furion/RemoteRequest/Proxies/HttpDispatchProxy.cs index 6250accde0..a7fc1d5f56 100644 --- a/framework/Furion/RemoteRequest/Proxies/HttpDispatchProxy.cs +++ b/framework/Furion/RemoteRequest/Proxies/HttpDispatchProxy.cs @@ -197,19 +197,38 @@ public class HttpDispatchProxy : AspectDispatchProxy, IDispatchProxy httpRequestPart.SetBody(bodyParameter.Value, bodyAttribute.ContentType, Encoding.GetEncoding(bodyAttribute.Encoding)); } - // 查找所有贴了 [BodyBytes] 特性的参数 - var bodyBytesParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(BodyBytesAttribute), true)); - if (bodyBytesParameters != null) + // 查找所有 HttpFile 和 HttpFile 集合类型的参数 + var filesParameters = parameters.Where(u => u.Parameter.ParameterType == typeof(HttpFile) + || u.Parameter.ParameterType == typeof(HttpFile[]) + || (u.Parameter.ParameterType.HasImplementedRawGeneric(typeof(IEnumerable<>)) && u.Parameter.ParameterType.GenericTypeArguments[0] == typeof(HttpFile))); + + if (filesParameters != null) { - var bodyBytes = new List<(string Name, byte[] Bytes, string FileName)>(); + var files = new List(); - foreach (var item in bodyBytesParameters) + foreach (var item in filesParameters) { - var bodyBytesAttribute = item.Parameter.GetCustomAttribute(); - if (item.Value != null && item.Value.GetType() == typeof(byte[])) bodyBytes.Add((bodyBytesAttribute.Alias ?? item.Name, (byte[])item.Value, bodyBytesAttribute.FileName)); + if (item.Value != null) + { + // 处理 HttpFile[] 类型 + if (item.Parameter.ParameterType.IsArray) + { + files.AddRange((HttpFile[])item.Value); + } + // 处理 IList IEnumerable 类型 + else if (typeof(IEnumerable).IsAssignableFrom(item.Parameter.ParameterType)) + { + files.AddRange(((IEnumerable)item.Value).Cast()); + } + // 处理单个类型 + else + { + files.Add((HttpFile)item.Value); + } + } } - httpRequestPart.SetBodyBytes(bodyBytes.ToArray()); + httpRequestPart.SetFiles(files.ToArray()); } } diff --git a/handbook/docs/http.mdx b/handbook/docs/http.mdx index a69648e6f8..d36e51806b 100644 --- a/handbook/docs/http.mdx +++ b/handbook/docs/http.mdx @@ -251,7 +251,6 @@ await "get".SetClient("github"); await "https://www.furion.icu".SetClient(() => new HttpClient()); ``` - ### 19.4.8 设置 `Body` 参数 ```cs showLineNumbers @@ -409,9 +408,15 @@ var str = await "https://www.baidu.com".GetAsStringAsync(); ### 19.4.19 设置 `Byte[]` 类型/上传文件 +:::warning `Furion 4.4.0` 以下版本 + +在 `Furion 4.4.0+` 版本移除了 `.SetBodyBytes` 方式,原因是拓展性太差,**新版本请使用 `.SetFiles` 方式**。 + +::: + 有时候我们需要上传文件,需要设置 `Content-Type` 为 `multipart/form-data` 类型,如: -```cs showLineNumbers +```cs showLineNumbers {3,7} // bytes 可以通过 File.ReadAllBytes(文件路径) 获取 var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") .SetBodyBytes(("键", bytes, "文件名")).PostAsync(); @@ -434,6 +439,22 @@ var result = await $"https://api.weixin.qq.com/wxa/img_sec_check?access_token={t ::: +:::tip `Furion 4.4.0+` 版本 + +如果使用 `Furion 4.4.0+` 版本,请使用以下的 `.SetFiles` 替代 `.SetBodyBytes` 操作。 + +::: + +```cs showLineNumbers {3,7} +// bytes 可以通过 File.ReadAllBytes(文件路径) 获取 +var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") + .SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync(); + +// 支持多个文件 +var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") + .SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync(); +``` + ### 19.4.20 设置 `IServiceProvider` 有时候我们需要构建一个作用域的 `IServiceProvider`,这时只需要设置即可: @@ -629,7 +650,6 @@ public interface IHttp : IHttpDispatchProxy } ``` - ### 19.5.6 设置 `Body` 参数 ```cs showLineNumbers {3,6,9} @@ -889,6 +909,12 @@ public interface IHttp : IHttpDispatchProxy ### 19.5.15 设置 `Byte[]` 类型/上传文件 +:::warning `Furion 4.4.0` 以下版本 + +在 `Furion 4.4.0+` 版本移除了 `[BodyBytes]` 方式,原因是拓展性太差,**新版本请使用 `HttpFile` 方式**。 + +::: + 有时候我们需要上传文件,需要设置 `Content-Type` 为 `multipart/form-data` 类型,如: ```cs showLineNumbers {3,4} @@ -903,6 +929,28 @@ public interface IHttp : IHttpDispatchProxy } ``` +:::tip `Furion 4.4.0+` 版本 + +如果使用 `Furion 4.4.0+` 版本,请使用以下的 `HttpFile` 替代 `[BodyBytes]` 操作。 + +::: + +```cs showLineNumbers {3-4,7-8,11-12} +public interface IHttp : IHttpDispatchProxy +{ + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(HttpFile file); + + // 支持多个文件 + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(HttpFile[] files); + + // 支持多个文件 + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(IList files); +} +``` + ### 19.5.16 设置 `Timeout` 超时时间 ```cs showLineNumbers diff --git a/handbook/docs/upgrade.mdx b/handbook/docs/upgrade.mdx index 5d0524e15d..d57afbb9b9 100644 --- a/handbook/docs/upgrade.mdx +++ b/handbook/docs/upgrade.mdx @@ -29,13 +29,42 @@ import useBaseUrl from "@docusaurus/useBaseUrl"; - **突破性变化** + - [调整] 远程请求 `.SetBodyBytes` 为 `.SetFiles` [#I5PMS5](https://gitee.com/dotnetchina/Furion/issues/I5PMS5) [#I5PIYI](https://gitee.com/dotnetchina/Furion/issues/I5PIYI) + - [移除] 远程请求 `[BodyBytes]` 设计,采用 `HttpFile` 方式 [#I5PMS5](https://gitee.com/dotnetchina/Furion/issues/I5PMS5) [#I5PIYI](https://gitee.com/dotnetchina/Furion/issues/I5PIYI) + +```cs showLineNumbers {3-4,7-8,11-12} +public interface IHttp : IHttpDispatchProxy +{ + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(HttpFile file); + + // 支持多个文件 + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(HttpFile[] files); + + // 支持多个文件 + [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] + Task PostXXXAsync(IList files); +} +``` + +```cs showLineNumbers {3,7} +// bytes 可以通过 File.ReadAllBytes(文件路径) 获取 +var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") + .SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync(); + +// 支持多个文件 +var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data") + .SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync(); +``` + - **问题修复** - **其他更改** - **文档** - - [更新] 友好异常文档 + - [更新] 友好异常文档、远程请求文档 --- diff --git a/samples/Furion.Application/IHttp.cs b/samples/Furion.Application/IHttp.cs index c60d71c2f3..a1afc46772 100644 --- a/samples/Furion.Application/IHttp.cs +++ b/samples/Furion.Application/IHttp.cs @@ -5,10 +5,10 @@ namespace Furion.Application; public interface IHttp : IBase { [Post("https://localhost:44316/api/test-module/upload-file", ContentType = "multipart/form-data")] - Task TestSingleFileProxyAsync([BodyBytes("file", "image.png")] Byte[] bytes); + Task TestSingleFileProxyAsync(HttpFile file); [Post("https://localhost:44316/api/test-module/upload-muliti-file", ContentType = "multipart/form-data")] - Task TestMultiFileProxyAsync([BodyBytes("files", "image.png")] Byte[] bytes, [BodyBytes("files", "image2.png")] Byte[] bytes2); + Task TestMultiFileProxyAsync(HttpFile[] files); } diff --git a/samples/Furion.Application/TestModuleServices.cs b/samples/Furion.Application/TestModuleServices.cs index 6b80912015..af2d712bd6 100644 --- a/samples/Furion.Application/TestModuleServices.cs +++ b/samples/Furion.Application/TestModuleServices.cs @@ -1,4 +1,5 @@ using Furion.Application.Persons; +using Furion.RemoteRequest; using Furion.RemoteRequest.Extensions; using Furion.UnifyResult; @@ -32,7 +33,7 @@ public class TestModuleServices : IDynamicApiController public async Task TestSingleFileProxy() { var bytes = File.ReadAllBytes("image.png"); - var result = await _http.TestSingleFileProxyAsync(bytes); + var result = await _http.TestSingleFileProxyAsync(HttpFile.Create("file", bytes, "image.png")); var fileName = await result.Content.ReadAsStringAsync(); return fileName; @@ -45,7 +46,7 @@ public class TestModuleServices : IDynamicApiController public async Task TestMultiFileProxy() { var bytes = File.ReadAllBytes("image.png"); - var result = await _http.TestMultiFileProxyAsync(bytes, bytes); + var result = await _http.TestMultiFileProxyAsync(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))); var fileName = await result.Content.ReadAsStringAsync(); return fileName; @@ -60,7 +61,7 @@ public class TestModuleServices : IDynamicApiController var bytes = File.ReadAllBytes("image.png"); var result = await "https://localhost:44316/api/test-module/upload-file".SetContentType("multipart/form-data") - .SetBodyBytes(("file", bytes, "image.png")).PostAsync(); + .SetFiles(HttpFile.Create("file", bytes, "image.png")).PostAsync(); var fileName = await result.Content.ReadAsStringAsync(); @@ -75,7 +76,7 @@ public class TestModuleServices : IDynamicApiController { var bytes = File.ReadAllBytes("image.png"); var result = await "https://localhost:44316/api/test-module/upload-muliti-file".SetContentType("multipart/form-data") - .SetBodyBytes(("files", bytes, "image.png"), ("files", bytes, "image2.png")).PostAsync(); + .SetFiles(HttpFile.CreateMultiple("files", (bytes, "image1.png"), (bytes, "image2.png"))).PostAsync(); var fileName = await result.Content.ReadAsStringAsync(); return fileName; -- Gitee