⭐️ Furion v4 版本支持【所有历史版本】无缝升级,一套代码兼容 .NET 5+ ⭐️
Skip to main content

19. 远程请求

版本说明

以下内容仅限 Furion 1.16.0 + 版本使用。

19.1 关于远程请求

在互联网大数据的驱动下,平台或系统免不了需要和第三方进行数据交互,而第三方往往提供了 RESTful API 接口规范,这个时候就需要通过 Http 请求第三方接口进行数据传输交互。

也就是本章节所说的远程请求。

19.2 远程请求的作用

  • 跨系统、跨设备通信
  • 实现多个系统数据传输交互
  • 跨编程语言协同开发

19.3 基础使用

19.3.1 注册服务

使用之前需在 Startup.cs 注册 远程请求服务

public void ConfigureServices(IServiceCollection services){    services.AddRemoteRequest();}

19.3.2 使用方式

Furion 提供两种方式访问发送远程请求。

定义代理请求的 接口 并继承 IHttpDispatchProxy 接口

public interface IHttp : IHttpDispatchProxy{    [Get("https://www.furion.icu/get")]    Task<Result> GetXXXAsync();    [Post("https://www.furion.icu/post")]    Task<Result> PostXXXAsync();    [Put("https://www.furion.icu/put")]    Task<Result> PutXXXAsync();    [Delete("https://www.furion.icu/delete")]    Task<Result> DeleteXXXAsync();    [Patch("https://www.furion.icu/patch")]    Task<Result> PatchXXXAsync();    [Head("https://www.furion.icu/head")]    Task<Result> HeadXXXAsync();}

通过构造函数注入 接口

using Furion.DynamicApiController;using Furion.RemoteRequest.Extensions;namespace Furion.Application{    public class RemoteRequestService : IDynamicApiController    {        private readonly IHttp _http;        public RemoteRequestService(IHttp http)        {            _http = http;        }        public async Task GetData()        {            var data = await _http.GetXXXAsync();        }    }}

19.4 字符串方式使用示例

19.4.1 内置请求方式

// 发送 Get 请求var response = await "https://www.furion.icu/get".GetAsync();// 发送 Post 请求var response = await "https://www.furion.icu/post".PostAsync();// 发送 Put 请求var response = await "https://www.furion.icu/put".PutAsync();// 发送 Delete 请求var response = await "https://www.furion.icu/delete".DeleteAsync();// 发送 Patch 请求var response = await "https://www.furion.icu/patch".PatchAsync();// 发送 Head 请求var response = await "https://www.furion.icu/head".HeadAsync();// 手动指定发送特定请求var response = await "https://www.furion.icu/post".SetHttpMethod(HttpMethod.Post)                                                  .SendAsync();

19.4.2 设置请求地址

// 该方式在 Furion v3.0.0 已移除,多此一举了await "".SetRequestUrl("https://www.furion.icu/");

19.4.3 设置请求方式

await "https://www.furion.icu/post".SetHttpMethod(HttpMethod.Get);

19.4.4 设置地址模板

// 字典方式await "https://www.furion.icu/post/{id}?name={name}&id={p.Id}".SetTemplates(new Dictionary<string , object> {    { "id", 1 },    { "name", "Furion" },    { "p.Id", new Person { Id = 1 } }});// 对象/匿名对象方式await "https://www.furion.icu/post/{id}?name={name}".SetTemplates(new {    id = 1,    name = "Furion"});

注:模板替换区分大小写。

19.4.5 设置请求报文头

// 字典方式await "https://www.furion.icu/post".SetHeaders(new Dictionary<string , object> {    { "Authorization", "Bearer 你的token"},    { "X-Authorization", "Bearer 你的刷新token"}});// 对象/匿名对象方式await "https://www.furion.icu/post".SetHeaders(new {    Authorization = "Bearer 你的token"});

19.4.6 设置 URL 地址参数

// 字典方式await "https://www.furion.icu/get".SetQueries(new Dictionary<string , object> {    { "id", 1 },    { "name", "Furion"}});// 对象/匿名对象方式await "https://www.furion.icu/get".SetQueries(new {    id = 1,    name = "Furion"});

最终输出格式为:https://www.furion.icu/get?id=1&name=Furion

19.4.7 设置请求客户端

  • 全局配置方式
services.AddRemoteRequest(options=>{    // 配置 Github 基本信息    options.AddHttpClient("github", c =>    {        c.BaseAddress = new Uri("https://api.github.com/");        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");        c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");    });});await "get".SetClient("github");

最终生成请求地址为:https://api.github.com/get

  • 局部配置方式
版本说明

以下内容仅限 Furion 4.3.8 + 版本使用。

await "https://www.furion.icu".SetClient(() => new HttpClient());

19.4.8 设置 Body 参数

// 传入对象await "https://www.furion.icu/api/user/add".SetBody(new User { Id = 1, Name = "Furion" });// 配置 Content-Typeawait "https://www.furion.icu/api/user/add".SetBody(new { Id = 1, Name = "Furion" }, "application/json");// 设置 Encoding 编码await  "https://www.furion.icu/api/user/add".SetBody(new User { Id = 1, Name = "Furion" }, "application/json", Encoding.UTF8);// 处理 application/x-www-form-urlencoded 请求await "https://www.furion.icu/api/user/add".SetBody(new Dictionary<string , object> {    { "Id", 1 },    { "Name", "Furion"}}, "application/x-www-form-urlencoded");// 处理 application/xml、text/xmlawait "https://www.furion.icu/api/user/add".SetBody("<SomeDto><SomeTag>somevalue</SomeTag></SomeDto>", "application/xml");
特别注意

如果请求 Content-Type 设置为 application/x-www-form-urlencoded 类型,那么底层自动将数据进行 UrlEncode 编码处理,无需外部处理。

19.4.9 设置 Content-Type

await "https://www.furion.icu/post".SetContentType("application/json");

19.4.10 设置内容编码

await "https://www.furion.icu/post".SetContentEncoding(Encoding.UTF8);

19.4.11 设置 JSON 序列化提供程序

Furion 默认情况下采用 System.Text.Json 进行 JSON 序列化处理,如需设置第三方 JSON 提供器,则可以通过以下配置:

// 泛型方式await "https://www.furion.icu/api/user/add".SetJsonSerialization<NewtonsoftJsonSerializerProvider>();// 非泛型方式await "https://www.furion.icu/api/user/add".SetJsonSerialization(typeof(NewtonsoftJsonSerializerProvider));// 添加更多配置await "https://www.furion.icu/api/user/add".SetJsonSerialization<NewtonsoftJsonSerializerProvider>(new JsonSerializerSettings {});// 比如配置缺省的序列化选项await "https://www.furion.icu".SetJsonSerialization(default, new JsonSerializerOptions    {        // 中文乱码        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping    })    .GetAsAsync();
关于 JSON 序列化提供器

如需了解更多 JSON 序列化知识可查阅 23. JSON 序列化 章节

19.4.12 启用 Body 参数验证

await "https://www.furion.icu/api/user/add".SetValidationState();// 设置不验证 null 值await "https://www.furion.icu/api/user/add".SetValidationState(includeNull: true);

支持类中 [Required] 等完整模型验证特性。

19.4.13 请求拦截

await "https://www.furion.icu/".OnRequesting((client, req) => {    // req 为 HttpRequestMessage 对象    // 追加更多参数    req.AppendQueries(new Dictionary<string, object> {        { "access_token", "xxxx"}    });});

支持多次拦截

19.4.14 HttpClient 拦截

await "https://www.furion.icu/".OnClientCreating(client => {    // client 为 HttpClient 对象    client.Timeout = 30000; // 设置超时时间});

支持多次拦截

19.4.15 请求之前拦截

await "https://www.furion.icu/".OnRequesting((client, req) => {    // req 为 HttpRequestMessage 对象});

支持多次拦截

19.4.16 成功请求拦截

await "https://www.furion.icu/".OnResponsing((client, res) => {    // res 为 HttpResponseMessage 对象});

支持多次拦截

19.4.17 请求异常拦截

await "https://www.furion.icu/".OnException((client, res, errors) => {    // res 为 HttpResponseMessage 对象});

支持多次拦截

19.4.18 各种返回值处理

Furion 远程请求默认提供四种返回值类型:

  • HttpResponseMessage:请求响应消息类型
  • Stream:流类型
  • T:泛型 T 类型
  • String:字符串类型,也就是直接将网络请求结果内容字符串化
  • Byte[]:字节数组类型

如:

// HttpResponseMessagevar res = await "https://www.furion.icu/".GetAsync();// Streamvar (stream, encoding) = await "https://www.furion.icu/".GetAsStreamAsync();// Tvar user = await "https://www.furion.icu/".GetAsAsync<User>();// Stringvar str = await "https://www.baidu.com".GetAsStringAsync();

19.4.19 设置 Byte[] 类型/上传文件

Furion 4.4.0 以下版本

Furion 4.4.0+ 版本移除了 .SetBodyBytes 方式,原因是拓展性太差,新版本请使用 .SetFiles 方式

有时候我们需要上传文件,需要设置 Content-Typemultipart/form-data 类型,如:

// bytes 可以通过 File.ReadAllBytes(文件路径) 获取var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data")                                               .SetBodyBytes(("键", bytes, "文件名")).PostAsync();// 支持多个文件var res = await "https://www.furion.icu/upload".SetContentType("multipart/form-data")                                               .SetBodyBytes(("键", bytes, "文件名"),("键", bytes, "文件名")).PostAsync();
关于微信上传接口

如果遇到微信上传出现问题,则可设置 Content-Type 为:application/octet-stream,如:

var result = await $"https://api.weixin.qq.com/wxa/img_sec_check?access_token={token}"                .SetBodyBytes(("media", bytes, Path.GetFileName(imgPath)))                .SetContentType("application/octet-stream")                .PostAsStringAsync();
Furion 4.4.0+ 版本

如果使用 Furion 4.4.0+ 版本,请使用以下的 .SetFiles 替代 .SetBodyBytes 操作。

// 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,这时只需要设置即可:

var res = await "https://www.furion.icu/upload".SetRequestScoped(services);

19.4.21 设置 Timeout 超时时间

var res = await "https://www.furion.icu/upload".SetClientTimeout(10); // 单位秒

19.4.22 支持模板配置

模板格式为:#(配置路径)

var res = await "#(Furion:Address)/upload".GetAsync();
{  "Furion": {    "Address": "https://www.furion.icu"  }}

19.4.23 重试策略

Furion v2.18+ 版本支持配置重试策略,如:

var res = await "https://www.furion.icu".SetRetryPolicy(3, 1000).GetAsync();

以上代码表示请求失败重试 3 次,每次延迟 1000ms

19.4.24 支持 GZip 压缩

Furion v3.2.0+ 版本支持GZip 压缩,如:

var res = await "https://www.furion.icu".WithGZip().GetAsync();

19.4.25 设置 Url 转码

过去版本会对所有的 Url 进行 Uri.EscapeDataString 转码,在 Furion v3.8.0+ 版本支持 Url 转码设置,如:

var res = await "https://www.furion.icu".WithEncodeUrl(false).GetAsync();

19.5 IHttpDispatchProxy 代理方式

19.5.1 支持多种代理方式

public interface IHttp : IHttpDispatchProxy{    // 发送 Get 请求    [Get("https://www.furion.icu/get")]    Task<HttpResponseMessage> GetXXXAsync();    // 发送 Post 请求    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync();    // 发送 Put 请求    [Put("https://www.furion.icu/put")]    Task<HttpResponseMessage> PutXXXAsync();    // 发送 Delete 请求    [Delete("https://www.furion.icu/delete")]    Task<HttpResponseMessage> DeleteXXXAsync();    // 发送 Patch 请求    [Patch("https://www.furion.icu/patch")]    Task<HttpResponseMessage> PatchXXXAsync();    // 发送 Head 请求    [Head("https://www.furion.icu/head")]    Task<HttpResponseMessage> HeadXXXAsync();}

19.5.2 设置地址模板

public interface IHttp : IHttpDispatchProxy{    [Get("https://www.furion.icu/get/{id}?name={name}&number={p.PersonDetail.PhonNumber}")]    Task<HttpResponseMessage> GetXXXAsync(int id, string name, Person p);}

注:模板替换区分大小写。

19.5.3 设置请求报文头

Furion 框架远程请求代理模式提供三种方式设置请求报文头:

  • 支持在接口中声明
  • 支持在方法中声明
  • 支持在参数中声明
[Headers("key","value")][Headers("key1","value2")] // 设置多个public interface IHttp : IHttpDispatchProxy{    [Get("https://www.furion.icu/get/{id}?name={name}"), Headers("key2","value2")]    Task<HttpResponseMessage> GetXXXAsync(int id, string name);    [Get("https://www.furion.icu")]    Task<HttpResponseMessage> GetXXX2Async(int id, [Headers]string token = default);    [Get("https://www.furion.icu")]    Task<HttpResponseMessage> GetXXX2Async(int id, string name, [Headers("别名")]string token = default);}

19.5.4 设置 URL 地址参数

public interface IHttp : IHttpDispatchProxy{    [Get("https://www.furion.icu/get/{id}?name={name}")]    Task<HttpResponseMessage> GetXXXAsync(int id, string name);    [Get("https://www.furion.icu/get/{p.Id}?name={p.Name}")]    Task<HttpResponseMessage> GetXXXAsync(Person p);    [Get("https://www.furion.icu/get")]    Task<HttpResponseMessage> GetXXXAsync([QueryString]int id, [QueryString]string name);    [Get("https://www.furion.icu/get")]    Task<HttpResponseMessage> GetXXXAsync([QueryString]int id, [QueryString("别名")]string name);}

最终输出格式为:https://www.furion.icu/get?id=1&name=Furion

19.5.5 设置请求客户端

  • 全局配置方式
services.AddRemoteRequest(options=>{    // 配置 Github 基本信息    options.AddHttpClient("github", c =>    {        c.BaseAddress = new Uri("https://api.github.com/");        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");        c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");    });});public interface IHttp : IHttpDispatchProxy{    [Get("get"), Client("github")]    Task<HttpResponseMessage> GetXXXAsync();}

最终生成请求地址为:https://api.github.com/get

  • 局部配置方式
版本说明

以下内容仅限 Furion 4.3.8 + 版本使用。

public interface IHttp : IHttpDispatchProxy{    // 局部方式    [Get("get")]    Task<HttpResponseMessage> GetXXXAsync([Interceptor(InterceptorTypes.Initiate)]Func<HttpClient> clientProvider);    // 全局静态方式    [Interceptor(InterceptorTypes.Initiate)]    static HttpClient CreateHttpClient()    {        return new HttpClient(...);    }}

19.5.6 设置 Body 参数

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Body]User user);    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Body("application/x-www-form-urlencoded")]User user);    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Body("application/x-www-form-urlencoded", "UTF-8")]User user);}

19.5.7 设置 JSON 序列化提供程序

Furion 默认情况下采用 System.Text.Json 进行 JSON 序列化处理,如需设置第三方 JSON 提供器,则可以通过以下配置:

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/post"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]    Task<HttpResponseMessage> PostXXXAsync([Body]User user);    [Post("https://www.furion.icu/post"), JsonSerialization(typeof(NewtonsoftJsonSerializerProvider))]    Task<HttpResponseMessage> PostXXXAsync([Body]User user, [JsonSerializerOptions]object jsonSerializerOptions = default);    /// <summary>    /// 缺省序列化配置    /// </summary>    /// <returns></returns>    [JsonSerializerOptions]    static object GetJsonSerializerOptions()    {        // 这里也可以通过 JSON.GetSerializerOptions<JsonSerializerOptions>() 获取 Startup.cs 中的配置        return new JsonSerializerOptions        {        };    }}

[JsonSerializerOptions] 可以标记参数是一个 JSON 序列化配置参数。

关于 JSON 序列化提供器

如需了解更多 JSON 序列化知识可查阅 23. JSON 序列化 章节

19.5.8 参数验证

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Range(1,10)]int id, [Required, MaxLength(10)]string name);    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Required]User user);  // 对象类型支持属性配置特性验证}

19.5.9 请求拦截

Furion 远程请求代理方式提供两种拦截方式:

  • 接口静态方法拦截
  • 参数标记拦截
public interface IHttp : IHttpDispatchProxy{    // 通过参数拦截    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Interceptor(InterceptorTypes.Request)] Action<HttpClient, HttpRequestMessage> action = default);    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Request)]    static void OnRequesting1(HttpClient client, HttpRequestMessage req)    {        // 追加更多参数        req.AppendQueries(new Dictionary<string, object> {            { "access_token", "xxxx"}        });    }    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Request)]    static void OnRequesting2(HttpClient client, HttpRequestMessage req)    {    }}

支持多次拦截

19.5.10 HttpClient 拦截

Furion 远程请求代理方式提供两种拦截方式:

  • 接口静态方法拦截
  • 参数标记拦截
public interface IHttp : IHttpDispatchProxy{    // 通过参数拦截    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Interceptor(InterceptorTypes.Client)] Action<HttpClient> action = default);    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Client)]    static void onClientCreating1(HttpClient client)    {    }    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Client)]    static void onClientCreating2(HttpClient client)    {    }}

支持多次拦截

19.5.11 请求之前拦截

Furion 远程请求代理方式提供两种拦截方式:

  • 接口静态方法拦截
  • 参数标记拦截
public interface IHttp : IHttpDispatchProxy{    // 通过参数拦截    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Interceptor(InterceptorTypes.Request)] Action<HttpClient, HttpRequestMessage> action = default);    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Request)]    static void OnRequest1(HttpClient client, HttpRequestMessage req)    {    }    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Request)]    static void OnRequest2(HttpClient client, HttpRequestMessage req)    {    }}

支持多次拦截

19.5.12 成功请求拦截

Furion 远程请求代理方式提供两种拦截方式:

  • 接口静态方法拦截
  • 参数标记拦截
public interface IHttp : IHttpDispatchProxy{    // 通过参数拦截    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Interceptor(InterceptorTypes.Response)] Action<HttpClient, HttpResponseMessage> action = default);    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Response)]    static void OnResponsing1(HttpClien client, HttpResponseMessage res)    {    }    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Response)]    static void OnResponsing2(HttpClien client, HttpResponseMessage res)    {    }}

支持多次拦截

19.5.13 请求异常拦截

Furion 远程请求代理方式提供两种拦截方式:

  • 接口静态方法拦截
  • 参数标记拦截
public interface IHttp : IHttpDispatchProxy{    // 通过参数拦截    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync([Interceptor(InterceptorTypes.Exception)] Action<HttpClient, HttpResponseMessage, string> action = default);    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Exception)]    static void OnException1(HttpClient client, HttpResponseMessage res, string errors)    {    }    // 全局拦截,类中每一个方法都会触发    [Interceptor(InterceptorTypes.Exception)]    static void OnException2(HttpClient client, HttpResponseMessage res, string errors)    {    }}

支持多次拦截

19.5.14 各种返回值处理

Furion 远程请求默认提供四种返回值类型:

  • HttpResponseMessage:请求响应消息类型
  • Stream:流类型
  • T:泛型 T 类型
  • String:字符串类型,也就是直接将网络请求结果内容字符串化

如:

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/post")]    Task<HttpResponseMessage> PostXXXAsync();    [Post("https://www.furion.icu/post")]    Task<Stream> PostXXXAsync();    [Post("https://www.furion.icu/post")]    Task<User> PostXXXAsync();    [Post("https://www.furion.icu/post")]    Task<string> PostXXXAsync();}

19.5.15 设置 Byte[] 类型/上传文件

Furion 4.4.0 以下版本

Furion 4.4.0+ 版本移除了 [BodyBytes] 方式,原因是拓展性太差,新版本请使用 HttpFile 方式

有时候我们需要上传文件,需要设置 Content-Typemultipart/form-data 类型,如:

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] // bytes 可以通过 File.ReadAllBytes(文件路径) 获取    Task<HttpResponseMessage> PostXXXAsync([BodyBytes("键","文件名")]Byte[] bytes);    // 支持多个文件    [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")] // bytes 可以通过 File.ReadAllBytes(文件路径) 获取    Task<HttpResponseMessage> PostXXXAsync([BodyBytes("键","文件名")]Byte[] bytes,[BodyBytes("键","文件名")]Byte[] bytes2);}
Furion 4.4.0+ 版本

如果使用 Furion 4.4.0+ 版本,请使用以下的 HttpFile 替代 [BodyBytes] 操作。

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")]    Task<HttpResponseMessage> PostXXXAsync(HttpFile file);    // 支持多个文件    [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")]    Task<HttpResponseMessage> PostXXXAsync(HttpFile[] files);    // 支持多个文件    [Post("https://www.furion.icu/upload", ContentType = "multipart/form-data")]    Task<HttpResponseMessage> PostXXXAsync(IList<HttpFile> files);}

19.5.16 设置 Timeout 超时时间

[Timeout(1000)]public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu/upload"), Timeout(500)]   // 单位秒    Task<HttpResponseMessage> PostXXXAsync([Body]User user);}

19.5.17 支持模板配置

模板格式为:#(配置路径)

public interface IHttp : IHttpDispatchProxy{    [Post("#(Furion:Address)/upload")]    Task<HttpResponseMessage> PostXXXAsync([Body]User user);}
{  "Furion": {    "Address": "https://www.furion.icu"  }}

方法的优先级高于接口定义的优先级。

19.5.18 重试策略

Furion v2.18+ 版本支持配置重试策略,如:

[RetryPolicy(3, 1000)] // 支持全局public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu"), RetryPolicy(3, 1000)]    // 支持局部    Task<HttpResponseMessage> PostXXXAsync([Body]User user);}

以上代码表示请求失败重试 3 次,每次延迟 1000ms

19.5.19 支持 GZip

Furion v3.2.0+ 版本支持 GZip,如:

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu", WithGZip = true)]    Task<HttpResponseMessage> PostXXXAsync([Body]User user);}

19.5.20 设置 Url 转码

过去版本会对所有的 Url 进行 Uri.EscapeDataString 转码,在 Furion v3.8.0+ 版本支持 Url 转码设置,如:

public interface IHttp : IHttpDispatchProxy{    [Post("https://www.furion.icu", WithEncodeUrl = false)]    Task<HttpResponseMessage> PostXXXAsync([Body]User user);}

19.6 请求客户端配置

Furion 框架也提供了多个请求客户端配置,可以为多个客户端请求配置默认请求信息,目前支持四种模式进行配置。

19.6.1 Startup.cs 统一配置

services.AddRemoteRequest(options=>{    // 配置默认 HttpClient    options.AddHttpClient(string.Empty, c => {        // 其他配置    });    // 配置特定客户端    options.AddHttpClient("github", c =>    {        c.BaseAddress = new Uri("https://api.github.com/");        c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");        c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");    });})

配置了命名客户端后,每次请求都会自动加上这些配置。

  • 代理请求 使用
[Get("api/getdata"), Client("github")]Task<User> GetData();[Put("api/getdata"), Client("facebook")]Task<User> GetData();
  • 字符串拓展 使用
// 设置请求拦截var response = await "http://47.100.247.61/api/sysdata/categories".SetClient("github").PostAsync();
  • IHttpClientFactory 中使用
public class ValuesController : Controller{    private readonly IHttpClientFactory _httpClientFactory;    public ValuesController(IHttpClientFactory httpClientFactory)    {        _httpClientFactory = httpClientFactory;    }    [HttpGet]    public async Task<ActionResult> Get()    {        var client = _httpClientFactory.CreateClient("github");        string result = await client.GetStringAsync("/");        return Ok(result);    }}

19.6.2 自定义 Client 类方式

我们可以按照一定的规则编写特定服务的请求客户端,如:

public class GitHubClient{    public HttpClient Client { get; private set; }    public GitHubClient(HttpClient httpClient)    {        httpClient.BaseAddress = new Uri("https://api.github.com/");        httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");        httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");        Client = httpClient;    }}

然后在 Startup.cs 中注册:

services.AddHttpClient<GitHubClient>();

使用如下:

public class ValuesController : Controller{    private readonly GitHubClient _gitHubClient;;    public ValuesController(GitHubClient gitHubClient)    {        _gitHubClient = gitHubClient;    }    [HttpGet]    public async Task<ActionResult> Get()    {        string result = await _gitHubClient.Client.GetStringAsync("/");        return Ok(result);    }}

19.6.3 自定义 Client 类 + 接口方式

我们也可以定义接口,通过接口的提供具体的服务 API 操作,无需手动配置 Url,如上面的 GetStringAsync("/")

public interface IGitHubClient{    Task<string> GetData();}public class GitHubClient : IGitHubClient{    private readonly HttpClient _client;    public GitHubClient(HttpClient httpClient)    {        httpClient.BaseAddress = new Uri("https://api.github.com/");        httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");        httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");        _client = httpClient;    }    public async Task<string> GetData()    {        return await _client.GetStringAsync("/");    }}

然后在 Startup.cs 中注册:

services.AddHttpClient<IGitHubClient, GitHubClient>();

使用:

public class ValuesController : Controller{    private readonly IGitHubClient _gitHubClient;;    public ValuesController(IGitHubClient gitHubClient)    {        _gitHubClient = gitHubClient;    }    [HttpGet]    public async Task<ActionResult> Get()    {        string result = await _gitHubClient.GetData();        return Ok(result);    }}

19.7 SSL/https 证书配置

有时候我们请求远程接口时会遇到 The SSL connection could not be established, see inner exception. 这样的错误,原因是证书配置不正确问题,下面有几种解决方法。

19.7.1 忽略特定客户端 SSL 证书检查

services.AddRemoteRequest(options=>{    // 默认 HttpClient 在 Furion 框架内部已经配置了该操作    options.AddHttpClient(string.Empty)            .ConfigurePrimaryHttpMessageHandler(u => new HttpClientHandler            {                ServerCertificateCustomValidationCallback = (_, _, _, _) => true,            });    // 配置特定客户端    options.AddHttpClient("github", c => { /*其他配置*/ })           .ConfigurePrimaryHttpMessageHandler(u => new HttpClientHandler            {                ServerCertificateCustomValidationCallback = (_, _, _, _) => true,            });});
关于 HttpClientHandlerSocketsHttpHandler

.NET6 之后默认使用 SocketsHttpHandler 作为默认底层网络通信,但比 HttpClientHandler 提供了更多平台无差异的功能,对 HttpClientHandler 的任何设置都会转发到 SocketsHttpHandler 中,如需使用 SocketsHttpHandler 配置可参考:

// 忽略 SSL 不安全检查,或 https 不安全或 https 证书有误options.AddHttpClient(string.Empty)       .ConfigurePrimaryHttpMessageHandler(u => new SocketsHttpHandler        {            SslOptions = new SslClientAuthenticationOptions            {                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true            }    });

19.7.2 手动指定 SSL 证书

services.AddRemoteRequest(options=>{    // 配置特定客户端    options.AddHttpClient("github", c => { /*其他配置*/ })           .ConfigurePrimaryHttpMessageHandler(u => new HttpClientHandler           {                // 手动配置证书                ClientCertificateOptions = ClientCertificateOption.Manual,                ClientCertificates = {                    new X509Certificate2("...","..."),                    new X509Certificate2("...","..."),                    new X509Certificate2("...","...")                }            });});

19.7.3 忽略所有客户端证书检查

版本说明

以下内容仅限 Furion v3.6.6+ 版本使用。

services.AddRemoteRequest(options=>{    // 需在所有客户端注册之前注册    options.ApproveAllCerts();});

19.8 关于返回值非 200 时忽略 Http状态

Furion 提供了非常方便的请求并且序列化请求结果 PostAsAsync<T>2.8.8 及以下版本,当返回结果的 Http 状态为非 200 时,会直接截断。考虑到请求接口的多样性,在 2.8.9 及以上版本增加忽略返回 Http 状态,直接序列化结果的方式。

//请求并且序列化请求结果var result = await "https://api.facebook.com/"    //如果不加OnException,则会直接截断    .OnException((res,errors)=> {        //激活异步拦截 此处可以做记录日志操作 也可保持现状    })    .PostAsAsync<T>();

PostAsStringAsync() 也使用同样的 OnException 操作使得忽略返回 Http 状态,原样返回 Http 请求结果

特别说明

如果不加 OnException,则会直接截断。 如果需要复杂的 Http Post 请求,建议直接使用 PostAsync,返回值为 HttpResponseMessage,可以更灵活的控制结果。

19.9 关于同步请求

Furion 框架内部默认不提供同步请求操作,建议总是使用异步的方式请求。如在不能使用异步的情况下,可自行转换为同步执行。如:

var result = "https://api.facebook.com".GetAsync().GetAwaiter().GetResult();

19.10 静态 Default 方式构建

await HttpRequestPart.Default.SetRequestUrl("https://www.baidu.com").GetAsStringAsync();

19.11 反馈与建议

与我们交流

给 Furion 提 Issue


了解更多

想了解更多 HttpClient 知识可查阅 ASP.NET Core - HTTP 请求 章节

演练场