搜索

查看: 3003|回复: 11

[ASP.NET] .net core 中 WebApiClientCore的使用示例代码

[复制链接]
发表于 2023-5-4 11:26:13 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:26:13 3003 11 看全部
WebApiClient
接口注册与选项
1 配置文件中配置HttpApiOptions选项
配置示例
"IUserApi": {
    "HttpHost": "http://www.webappiclient.com/",
    "UseParameterPropertyValidate": false,
    "UseReturnValuePropertyValidate": false,
    "JsonSerializeOptions": {
      "IgnoreNullValues": true,
      "WriteIndented": false
    }
  }
2 Service注册
示例
services
    .ConfigureHttpApi[I](Configuration.GetSection(nameof(IUserApi)))
    .ConfigureHttpApi[I](o =>
    {
        // 符合国情的不标准时间格式,有些接口就是这么要求必须不标准
        o.JsonSerializeOptions.Converters.Add(new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss"));
    });
HttpApiOptions详细展示
///
/// 表示HttpApi选项
///
public class HttpApiOptions
{
    ///
    /// 获取或设置Http服务完整主机域名
    /// 例如http://www.abc.com/或http://www.abc.com/path/
    /// 设置了HttpHost值,HttpHostAttribute将失效
    ///
    public Uri? HttpHost { get; set; }
    ///
    /// 获取或设置是否使用的日志功能
    ///
    public bool UseLogging { get; set; } = true;
    ///
    /// 获取或设置请求头是否包含默认的UserAgent
    ///
    public bool UseDefaultUserAgent { get; set; } = true;
    ///
    /// 获取或设置是否对参数的属性值进行输入有效性验证
    ///
    public bool . { get; set; } = true;
    ///
    /// 获取或设置是否对返回值的属性值进行输入有效性验证
    ///
    public bool UseReturnValuePropertyValidate { get; set; } = true;

    ///
    /// 获取json序列化选项
    ///
    public JsonSerializerOptions JsonSerializeOptions { get; } = CreateJsonSerializeOptions();
    ///
    /// 获取json反序列化选项
    ///
    public JsonSerializerOptions JsonDeserializeOptions { get; } = CreateJsonDeserializeOptions();
    ///
    /// xml序列化选项
    ///
    public XmlWriterSettings XmlSerializeOptions { get; } = new XmlWriterSettings();
    ///
    /// xml反序列化选项
    ///
    public XmlReaderSettings XmlDeserializeOptions { get; } = new XmlReaderSettings();
    ///
    /// 获取keyValue序列化选项
    ///
    public KeyValueSerializerOptions KeyValueSerializeOptions { get; } = new KeyValueSerializerOptions();
    ///
    /// 获取自定义数据存储的字典
    ///
    public Dictionary Properties { get; } = new Dictionary();
    ///
    /// 获取接口的全局过滤器集合
    ///
    public IList[I] GlobalFilters { get; } = new List[I]();

    ///
    /// 创建序列化JsonSerializerOptions
    ///  
    private static JsonSerializerOptions CreateJsonSerializeOptions()
    {
        return new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        };
    }
    ///
    /// 创建反序列化JsonSerializerOptions
    ///
    ///
    private static JsonSerializerOptions CreateJsonDeserializeOptions()
    {
        var options = CreateJsonSerializeOptions();
        options.Converters.Add(JsonCompatibleConverter.EnumReader);
        options.Converters.Add(JsonCompatibleConverter.DateTimeReader);
        return options;
    }
}
Uri(url)拼接规则
所有的Uri拼接都是通过Uri(Uri baseUri, Uri relativeUri)这个构造器生成。
带/结尾的baseUri
  • http://a.com/ + b/c/d = http://a.com/b/c/d
  • http://a.com/path1/ + b/c/d = http://a.com/path1/b/c/d
  • http://a.com/path1/path2/ + b/c/d = http://a.com/path1/path2/b/c/d
    不带/结尾的baseUri
  • http://a.com + b/c/d = http://a.com/b/c/d
  • http://a.com/path1 + b/c/d = http://a.com/b/c/d
  • http://a.com/path1/path2 + b/c/d = http://a.com/path1/b/c/d
    事实上http://a.com与http://a.com/是完全一样的,他们的path都是/,所以才会表现一样。为了避免低级错误的出现,请使用的标准baseUri书写方式,即使用/作为baseUri的结尾的第一种方式。
    OAuths&Token
    推荐使用自定义TokenProvider
    public class TestTokenProvider : TokenProvider
        {
            private readonly IConfiguration _configuration;
            public TestTokenProvider(IServiceProvider services,IConfiguration configuration) : base(services)
            {
                _configuration = configuration;
            }
            protected override Task RefreshTokenAsync(IServiceProvider serviceProvider, string refresh_token)
            {
               return this.RefreshTokenAsync(serviceProvider, refresh_token);
            }
            protected override async Task RequestTokenAsync(IServiceProvider serviceProvider)
            {
                LoginInput login = new LoginInput();
                login.UserNameOrEmailAddress = "admin";
                login.Password = "bb123456";
                var result = await serviceProvider.GetRequiredService[I]().RequestToken(login).Retry(maxCount: 3);
                return result;
            }
        }

    TokenProvider的注册
    services.AddTokenProvider[I]();
    OAuthTokenHandler
    可以自定义OAuthTokenHandler官方定义是属于http消息处理器,功能与OAuthTokenAttribute一样,除此之外,如果因为意外的原因导致服务器仍然返回未授权(401状态码),其还会丢弃旧token,申请新token来重试一次请求。
    OAuthToken在webapiclient中一般是保存在http请求的Header的Authrization
    当token在url中时我们需要自定义OAuthTokenHandler
    class UriQueryOAuthTokenHandler : OAuthTokenHandler
    {
        ///
        /// token应用的http消息处理程序
        ///
        /// token提供者
        public UriQueryOAuthTokenHandler(ITokenProvider tokenProvider)
            : base(tokenProvider)
        {
        }
        ///
        /// 应用token
        ///
        ///
        ///
        protected override void UseTokenResult(HttpRequestMessage request, TokenResult tokenResult)
        {
            // var builder = new UriBuilder(request.RequestUri);
            // builder.Query += "mytoken=" + Uri.EscapeDataString(tokenResult.Access_token);
            // request.RequestUri = builder.Uri;
            
            var uriValue = new UriValue(request.RequestUri).AddQuery("myToken", tokenResult.Access_token);
            request.RequestUri = uriValue.ToUri();
        }
    }

    AddQuery是请求的的url中携带token的key
    自定义OAuthTokenHandler的使用
    services
        .AddHttpApi[I]()
        .AddOAuthTokenHandler((s, tp) => new UriQueryOAuthTokenHandler(tp));
    //自定义TokoenProvider使用自定义OAuthTokenHandler
    apiBulider.AddOAuthTokenHandler[U]((sp,token)=>
                {
                    token=sp.GetRequiredService();
                    return new UrlTokenHandler(token);
                },WebApiClientCore.Extensions.OAuths.TypeMatchMode.TypeOrBaseTypes);
    OAuthToken 特性
    OAuthToken可以定义在继承IHttpApi的接口上也可以定义在接口的方法上
    在使用自定义TokenProvier时要注意OAuthToken特性不要定义在具有请求token的Http请求定义上
    Patch请求
    json patch是为客户端能够局部更新服务端已存在的资源而设计的一种标准交互,在RFC6902里有详细的介绍json patch,通俗来讲有以下几个要点:
  • 使用HTTP PATCH请求方法;
  • 请求body为描述多个opration的数据json内容;
  • 请求的Content-Type为application/json-patch+json;
    声明Patch方法
    public interface IUserApi
    {
        [HttpPatch("api/users/{id}")]
        Task[U] PatchAsync(string id, JsonPatchDocument[U] doc);
    }
    实例化JsonPatchDocument
    var doc = new JsonPatchDocument[U]();
    doc.Replace(item => item.Account, "laojiu");
    doc.Replace(item => item.Email, "laojiu@qq.com");
    请求内容
    PATCH /api/users/id001 HTTP/1.1
    Host: localhost:6000
    User-Agent: WebApiClientCore/1.0.0.0
    Accept: application/json; q=0.01, application/xml; q=0.01
    Content-Type: application/json-patch+json
    [{"op":"replace","path":"/account","value":"laojiu"},{"op":"replace","path":"/email","value":"laojiu@qq.com"}]
    异常处理
    try
    {
        var model = await api.GetAsync();
    }
    catch (HttpRequestException ex) when (ex.InnerException is ApiInvalidConfigException configException)
    {
        // 请求配置异常
    }
    catch (HttpRequestException ex) when (ex.InnerException is ApiResponseStatusException statusException)
    {
        // 响应状态码异常
    }
    catch (HttpRequestException ex) when (ex.InnerException is ApiException apiException)
    {
        // 抽象的api异常
    }
    catch (HttpRequestException ex) when (ex.InnerException is SocketException socketException)
    {
        // socket连接层异常
    }
    catch (HttpRequestException ex)
    {
        // 请求异常
    }
    catch (Exception ex)
    {
        // 异常
    }

    请求重试
    使用ITask异步声明,就有Retry的扩展,Retry的条件可以为捕获到某种Exception或响应模型符合某种条件。
    GetNumberTemplateForEditOutput put = new GetNumberTemplateForEditOutput();
                var res = await _testApi.GetForEdit(id).Retry(maxCount: 1).WhenCatchAsync(async p =>
                {
                    if (p.StatusCode == HttpStatusCode.Unauthorized)
                    {
                        await Token();//当http请求异常时报错,重新请求一次,保证token一直有效
                    }
                });
                put = res.Result;
                return put;
    API接口处理
    使用ITask异步声明
    [HttpHost("请求地址")]//请求地址域名
        public interface ITestApi : IHttpApi
        {
            [OAuthToken]//权限
            [JsonReturn]//设置返回格式
            [HttpGet("/api/services/app/NumberingTemplate/GetForEdit")]//请求路径
            ITask> GetForEdit([Required] string id);//请求参数声明
            [HttpPost("api/TokenAuth/Authenticate")]
            ITask RequestToken([JsonContent] AuthenticateModel login);
        }
    基于WebApiClient的扩展类
    扩展类声明
    ///
        /// WebApiClient扩展类
        ///
        public static class WebApiClientExentions
        {
            public static IServiceCollection AddWebApiClietHttp(this IServiceCollection services, Action? options = null) where THttp : class, IHttpApi
            {
                HttpApiOptions option = new HttpApiOptions();
                option.JsonSerializeOptions.Converters.Add(new JsonDateTimeConverter("yyyy-MM-dd HH:mm:ss"));
                option.UseParameterPropertyValidate = true;
                if(options != null)
                {
                    options.Invoke(option);
                }
                services.AddHttpApi().ConfigureHttpApi(p => p = option);
                return services;
            }
            public static IServiceCollection AddWebApiClietHttp(this IServiceCollection services,IConfiguration configuration) where THttp : class, IHttpApi
            {
                services.AddHttpApi().ConfigureHttpApi((Microsoft.Extensions.Configuration.IConfiguration)configuration);
                return services;
            }
            public static IServiceCollection AddWebApiClientHttpWithTokeProvider(this IServiceCollection services, Action? options = null) where THttp : class, IHttpApi
                where TTokenProvider : class, ITokenProvider
            {
                services.AddWebApiClietHttp(options);
                services.AddTokenProvider();
                return services;
            }
            public static IServiceCollection AddWebApiClientHttpWithTokeProvider(this IServiceCollection services, IConfiguration configuration) where THttp : class, IHttpApi
                where TTokenProvider : class, ITokenProvider
            {
                services.AddWebApiClietHttp(configuration);
                services.AddTokenProvider();
                return services;
            }
        }
    扩展类使用
    services.AddWebApiClientHttpWithTokeProvider[I]();
    到此这篇关于.net core 中 WebApiClientCore的使用示例代码的文章就介绍到这了,更多相关.net core 中 WebApiClientCore使用内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-29 14:35:56 | 显示全部楼层
    老橡树1 2023-6-29 14:35:56 看全部
    楼主太厉害了!楼主,I*老*虎*U!我觉得知鸟论坛真是个好地方!
    回复

    使用道具 举报

    发表于 2023-6-29 16:36:01 | 显示全部楼层
    Gordon520 2023-6-29 16:36:01 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-29 18:00:59 | 显示全部楼层
    123456819 2023-6-29 18:00:59 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 21:48:42 | 显示全部楼层
    123456848 2023-6-29 21:48:42 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 22:13:06 | 显示全部楼层
    伊索谗言 2023-6-29 22:13:06 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 23:24:34 | 显示全部楼层
    小妖花满楼满fx 2023-6-29 23:24:34 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-6-29 23:29:33 | 显示全部楼层
    xinting_6ym 2023-6-29 23:29:33 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-30 00:16:01 | 显示全部楼层
    123456823 2023-6-30 00:16:01 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    回复

    使用道具 举报

    发表于 2023-6-30 09:19:52 | 显示全部楼层
    462710480 2023-6-30 09:19:52 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表