我是WebApiClient库的做者,目前在开发其.netcore
版本,在整理其readme后,想一想一来这部份内容可能对你们有用,二来兴许能给WebApiClient带人更多人气,因此将readme做为博客在此发表。git
WebApiClient.JIT的.netcore版本,基于HttpClient的高性能与高可扩展性于一体的声明式Http客户端库,特别适用于微服务的restful资源请求,也适用于各类非标准的http接口请求。github
<PackageReference Include="WebApiClientCore" Version="1.0.0-beta1" />
System.Text.Json
替换Json.net
,提高序列化性能Microsoft.Extensions.Http的HttpClientFactory
WebApiClientCore、WebApiClient.JIT与原生HttpClient的性能比较,相比原生的HttpClient,WebApiClientCore几乎没有性能损耗。web
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.778 (1903/May2019Update/19H1)
Intel Core i3-4150 CPU 3.50GHz (Haswell), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.1.202
[Host] : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJITjson
Method | Mean | Error | StdDev |
---|---|---|---|
WebApiClient_GetAsync | 279.479 us | 22.5466 us | 64.3268 us |
WebApiClientCore_GetAsync | 25.298 us | 0.4953 us | 0.7999 us |
HttpClient_GetAsync | 2.849 us | 0.0568 us | 0.1393 us |
WebApiClient_PostAsync | 25.942 us | 0.3817 us | 0.3188 us |
WebApiClientCore_PostAsync | 13.462 us | 0.2551 us | 0.6258 us |
HttpClient_PostAsync | 4.515 us | 0.0866 us | 0.0926 us |
如下声明的代码为使用
WebApiClientCore.Extensions.OpenApi
工具将openApi文档反向生成获得api
namespace Petstore { /// <summary> /// Everything about your Pets /// </summary> [LoggingFilter] [HttpHost("https://petstore.swagger.io/v2/")] public interface IPetApi : IHttpApi { /// <summary> /// Add a new pet to the store /// </summary> /// <param name="body">Pet object that needs to be added to the store</param> [HttpPost("pet")] Task AddPetAsync([Required] [JsonContent] Pet body, CancellationToken token = default); /// <summary> /// Update an existing pet /// </summary> /// <param name="body">Pet object that needs to be added to the store</param> [HttpPut("pet")] Task<HttpResponseMessage> UpdatePetAsync([Required] [JsonContent] Pet body, CancellationToken token = default); /// <summary> /// Finds Pets by status /// </summary> /// <param name="status">Status values that need to be considered for filter</param> /// <returns>successful operation</returns> [HttpGet("pet/findByStatus")] ITask<List<Pet>> FindPetsByStatusAsync([Required] IEnumerable<Anonymous> status); /// <summary> /// Finds Pets by tags /// </summary> /// <param name="tags">Tags to filter by</param> /// <returns>successful operation</returns> [Obsolete] [HttpGet("pet/findByTags")] ITask<List<Pet>> FindPetsByTagsAsync([Required] [PathQuery] IEnumerable<string> tags); /// <summary> /// Find pet by ID /// </summary> /// <param name="petId">ID of pet to return</param> /// <returns>successful operation</returns> [HttpGet("pet/{petId}")] ITask<Pet> GetPetByIdAsync([Required] long petId); /// <summary> /// Updates a pet in the store with form data /// </summary> /// <param name="petId">ID of pet that needs to be updated</param> /// <param name="name">Updated name of the pet</param> /// <param name="status">Updated status of the pet</param> [HttpPost("pet/{petId}")] Task UpdatePetWithFormAsync([Required] long petId, [FormContent] string name, [FormContent] string status); /// <summary> /// Deletes a pet /// </summary> /// <param name="api_key"></param> /// <param name="petId">Pet id to delete</param> [HttpDelete("pet/{petId}")] Task DeletePetAsync([Header("api_key")] string api_key, [Required] long petId); /// <summary> /// uploads an image /// </summary> /// <param name="petId">ID of pet to update</param> /// <param name="additionalMetadata">Additional data to pass to server</param> /// <param name="file">file to upload</param> /// <returns>successful operation</returns> [LoggingFilter(Enable = false)] [HttpPost("pet/{petId}/uploadImage")] ITask<ApiResponse> UploadFileAsync([Required] long petId, [FormDataContent] string additionalMetadata, FormDataFile file); } }
另外一个例子是
WebApiClientCore.Extensions.OAuths.IOAuthClient
接口声明服务器
using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using WebApiClientCore.Attributes; namespace WebApiClientCore.Extensions.OAuths { /// <summary> /// 定义Token客户端的接口 /// </summary> [LoggingFilter] [XmlReturn(Enable = false)] [JsonReturn(EnsureMatchAcceptContentType = false, EnsureSuccessStatusCode = false)] public interface IOAuthClient { /// <summary> /// 以client_credentials受权方式获取token /// </summary> /// <param name="endpoint">token请求地址</param> /// <param name="credentials">身份信息</param> /// <returns></returns> [HttpPost] [FormField("grant_type", "client_credentials")] Task<TokenResult> RequestTokenAsync([Required, Uri] Uri endpoint, [Required, FormContent] ClientCredentials credentials); /// <summary> /// 以password受权方式获取token /// </summary> /// <param name="endpoint">token请求地址</param> /// <param name="credentials">身份信息</param> /// <returns></returns> [HttpPost] [FormField("grant_type", "password")] Task<TokenResult> RequestTokenAsync([Required, Uri] Uri endpoint, [Required, FormContent] PasswordCredentials credentials); /// <summary> /// 刷新token /// </summary> /// <param name="endpoint">token请求地址</param> /// <param name="credentials">身份信息</param> /// <returns></returns> [HttpPost] [FormField("grant_type", "refresh_token")] Task<TokenResult> RefreshTokenAsync([Required, Uri] Uri endpoint, [Required, FormContent] RefreshTokenCredentials credentials); } }
服务注册restful
var services = new ServiceCollection(); services.AddHttpApi<IPetApi>(o => { o.UseParameterPropertyValidate = true; o.UseReturnValuePropertyValidate = false; o.KeyValueSerializeOptions.IgnoreNullValues = true; o.HttpHost = new Uri("http://localhost:6000/"); });
服务获取app
public class MyService { private readonly IpetApi petApi; // 构造器注入IpetApi public MyService(IpetApi petApi) { tihs.petApi = petApi; } }
在整个Interface或某个Method上声明[LoggingFilter],便可把请求和响应的内容输出到LoggingFactory中。框架
若是要排除某个Method不打印日志(好比大流量传输接口),在方法上声明[LoggingFilter(Enable = false)],便可将本Method排除。异步
这个用于控制客户端但愿服务器返回什么样的内容格式,好比json或xml,默认的配置值是Accept: application/json; q=0.01, application/xml; q=0.01
若是想json优先,能够在Interface或Method上声明[JsonReturn],请求变为Accept: application/json, application/xml; q=0.01
若是想禁用其中一种,好比禁用xml,能够在Interface或Method上声明[XmlReturn(Enable = false)],请求变为Accept: application/json; q=0.01
使用ITask<>异步声明,就有Retry的扩展,Retry的条件能够为捕获到某种Exception或响应模型符合某种条件。
var result = await youApi.GetModelAsync(id: "id001") .Retry(maxCount: 3) .WhenCatch<Exception>() .WhenResult(r => r.ErrorCode > 0);
使用WebApiClientCore.Extensions.OAuths
扩展,轻松支持token的获取、刷新与应用
// 为接口注册与配置token提者选项 services.AddClientCredentialsTokenProvider<IpetApi>(o => { o.Endpoint = new Uri("http://localhost:6000/api/tokens"); o.Credentials.Client_id = "clientId"; o.Credentials.Client_secret = "xxyyzz"; });
/// <summary> /// 用户操做接口 /// </summary> [ClientCredentialsToken] public interface IpetApi { ... }
清空Token,未过时的token也强制刷新
var providers = serviceProvider.GetServices<ITokenProvider>(); foreach(var item in providers) { // 强制清除token以支持下次获取到新的token item.ClearToken(); }
自定义Token应用,获得token值,怎么用本身说了算
class MyTokenAttribute : ClientCredentialsTokenAttribute { protected override void UseTokenResult(ApiRequestContext context, TokenResult tokenResult) { context.HttpContext.RequestMessage.Headers.TryAddWithoutValidation("xxx-header", tokenResult.Access_token); } } /// <summary> /// 用户操做接口 /// </summary> [MyToken] public interface IpetApi { ... }