.net的retrofit--WebApiClient库深入篇
前言
本篇文章的內容是對上一篇.net的retrofit--WebApiClient庫的深層次補充,你可能需要先閱讀上一篇才能理解此篇文章。本文將詳細地講解WebApiClient的原理,結合實際項目中可能遇到的問題進行使用說明。
庫簡介
WebApiClient是開源在github上的一個httpClient客戶端庫,內部基于HttpClient開發,是一個只需要定義c#接口(interface),并打上相關特性,即可異步調用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。
1. HttpApiConfig的使用
1.1 創建HttpApiConfig
var config = new HttpApiConfig { ? ?// 請求的域名,會覆蓋[HttpHost]特性HttpHost = new Uri("http://www.webapiclient.com"), };var myWebApi = HttpApiClient.Create<MyWebApi>(config);
1.2 HttpApiConfig.FormatOptions
當序列化一個多屬性的模型時,FormatOptions可以約束DateTime類型的屬性值轉換為字符串的格式,也可以指定屬性名是為CamelCase。
1.3 HttpApiConfig.HttpClient
首次獲取HttpClient實例時,HttpClient的實例將被創建,HttpClient屬性是一個IHttpClient接口,是對HttpClient對象的包裝,它的Handler暴露出與HttpClient關聯的HttpClientHandler對象。
1.4 HttpApiConfig.GlobalFilters
GlobalFilters用于添加全局過濾器,這些過濾器不需要使用硬編碼修飾于接口,而是通過配置傳輸給接口的實例,適用于接口定義的項目和接口調用的項目分離開的項目結構。
1.5 HttpApiConfig的生命周期
在實例化HttpApiConfig之后,當不再使用時,應該顯性地調用Dispose釋放資源;
對于1.1的例子,如果myWebApi實現了IDisposable接口,調用myWebApi.Dispose()也會將HttpApiConfig的HttpClient屬性也釋放;
對于var myWebApi = HttpApiClient.Create
2.WebApiClient執行流程
1 創建接口實現類
當調用WebApiClient.Create時,內部使用Emit創建接口的實現類,該實現類為接口的每個方法實現為:獲取方法信息和調用參數值傳給攔截器(IApiInterceptor)處理;2 攔截器創建ITask任務
IApiInterceptor收到方法的調用時,根據方法信息和參數值創建Api描述對象ApiActionDescriptor,然后將和HttpApiConfig實例和ApiActionDescriptor包裝成ITask任務對象并返回;3 等待調用者執行請求
當調用者await ITask 或 await ITask.InvokeAsync()時,創建ApiActionContext并按照順序執行ApiActionContext里描述的各種Attribute,這些Attribue影響著ApiActionContext的HttpRequestMessage等屬性對象,然后使用HttpClient發送這個HttpRequestMessage對象,得到HttpResponseMessage,最后將HttpResponseMessage的Content轉換為接口的返回值;
/// <summary>
/// 異步執行api
/// </summary>
/// <param name="context">上下文</param>
/// <returns></returns>
public async Task<object> ExecuteAsync(ApiActionContext context)
{
? ? var apiAction = context.ApiActionDescriptor;
? ? var globalFilters = context.HttpApiConfig.GlobalFilters;
? ? foreach (var actionAttribute in apiAction.Attributes)
? ? {
? ? ? ? await actionAttribute.BeforeRequestAsync(context);
? ? }
? ? foreach (var parameter in apiAction.Parameters)
? ? {
? ? ? ? foreach (var parameterAttribute in parameter.Attributes)
? ? ? ? {
? ? ? ? ? ? await parameterAttribute.BeforeRequestAsync(context, parameter);
? ? ? ? }
? ? }
? ? foreach (var filter in globalFilters)
? ? {
? ? ? ? await filter.OnBeginRequestAsync(context);
? ? }
? ? foreach (var filter in apiAction.Filters)
? ? {
? ? ? ? await filter.OnBeginRequestAsync(context);
? ? }
? ? await this.SendAsync(context);
? ? foreach (var filter in globalFilters)
? ? {
? ? ? ? await filter.OnEndRequestAsync(context);
? ? }
? ? foreach (var filter in apiAction.Filters)
? ? {
? ? ? ? await filter.OnEndRequestAsync(context);
? ? }
? ? return await apiAction.Return.Attribute.GetTaskResult(context);
}
3.使用自定義特性
WebApiClient內置很多特性,包含接口級、方法級、參數級的,他們分別是實現了IApiActionAttribute接口、IApiActionFilterAttribute接口、IApiParameterAttribute接口、IApiParameterable接口和IApiReturnAttribute接口的一個或多個接口。一般情況下內置的特性就足以夠用,但實際項目中,你可能會遇到個別特殊的場景,需要自己實現一些特性或過濾器,主要用來操控請求上下文的RequestMessage對象,影響請求對象。
3.1 自定義IApiParameterAttribute例子
舉個例子:比如,服務端要求使用x-www-form-urlencoded提交,由于接口設計不合理,目前要求是提交:fieldX= {X}的json文本&fieldY={Y}的json文本 這里{X}和{Y}都是一個多字段的Model,我們對應的接口是這樣設計的:
[HttpHost("/upload")]ITask<bool> UploadAsync([FormField][AliasAs("fieldX")]
string xJson,[FormField][AliasAs("fieldY")]
string yJson);
顯然,我們接口參數為string類型的范圍太廣,沒有約束性,我們希望是這樣子:
[HttpHost("/upload")]ITask<bool> UploadAsync([FormFieldJson] X fieldX, [FormFieldJson] Y fieldY);
現在我們為這種特殊場景實現一個[FormFieldJson]的參數級特性,給每個參數修飾這個[FormFieldJson]后,參數就解釋為其序列化為Json的文本,做為表單的一個字段內容:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
class FormFieldJson: Attribute, IApiParameterAttribute
{
? ? public async Task BeforeRequestAsync(ApiActionContext context, ApiParameterDescriptor parameter)
? ? {
? ? ? ? var options = context.HttpApiConfig.FormatOptions;
? ? ? ? var json = context.HttpApiConfig.JsonFormatter.Serialize(parameter.Value, options);
? ? ? ? var fieldName = parameter.Name;
? ? ? ? await context.RequestMessage.AddFormFieldAsync(fieldName, json);
? ? }
}
3.2 自定義過濾器
舉個例子:我們需要為每個請求的url額外的動態添加一個叫sign的參數,這個sign可能和配置文件等有關系,而且每次都需要計算:
class SignFilter : ApiActionFilterAttribute
{
? ? public override Task OnBeginRequestAsync(ApiActionContext context)
? ? {
? ? ? ? var sign = DateTime.Now.Ticks.ToString();
? ? ? ? context.RequestMessage.AddUrlQuery("sign", sign);
? ? ? ? return base.OnBeginRequestAsync(context);
? ? }
}
[SignFilter]
public interface MyApi : IDisposable
{
? ? ...
}
3.3 自定義全局過濾器
class GlobalFilter : IApiActionFilter
{
? ? public Task OnBeginRequestAsync(ApiActionContext context)
? ? {
? ? ? ? if (context.ApiActionDescriptor.Member.IsDefined(typeof(MyCustomAttribute), true))
? ? ? ? {
? ? ? ? ? ? // do something
? ? ? ? }
? ? ? ? return Task.CompletedTask;
? ? }
? ? public Task OnEndRequestAsync(ApiActionContext context)
? ? {
? ? ? ? return Task.CompletedTask;
? ? }
}
// 通過配置項將全局過濾器傳給MyWebApi實例
var config = new HttpApiConfig();
config.GlobalFilters.Add(new GlobalFilter());
var myWebApi = HttpApiClient.Create<MyWebApi>(config);
4. DataAnnotations
在一些場景中,你的模型與服務需要的數據模塊可能不是全部吻合,DataAnnotations的功能可以非常方便實現兩者的對接,目前DataAnnotations只支持Json序列化和KeyValue序列化,xml序列化不受任何變化。
public class UserInfo
{
? ? public string Account { get; set; }
? ? // 別名
? ? [AliasAs("a_password")]
? ? public string Password { get; set; }
? ? // 時間格式,優先級最高
? ? [DateTimeFormat("yyyy-MM-dd")]
? ? public DateTime? BirthDay { get; set; }
? ??
? ? // 忽略序列化
? ? [IgnoreSerialized]
? ? public string Email { get; set; }?
? ??
? ? // 時間格式
? ? [DateTimeFormat("yyyy-MM-dd HH:mm:ss")]
? ? public DateTime CreateTime { get; set; }
}
5. 了解ITask對象
5.1 await ITask
await ITask,實際是調用了ITask.GetAwaiter()方法,等于同于ITask.InvokeAsync().GetAwaiter()方法。所以await ITask等同于await ITask.InvokeAsync()
5.2 ITask的InvokeAsync方法
InvokeAsync()返回Task對象,實際是http請求的任務對象。一個ITask實例,可以多次調用InvokeAsync()方法,完成多次一模一樣的請求。ITask的很多擴展,是對InvokeAsync方法調用的包裝而得到。
5.3 ITask的Retry和Handle
Retry本質上是對ITask的InvokeAsync的包裝,實際思想是當符合某種條件時,就多調用一次InvokeAsync方法,達到重試提交請求的目的。
Handle也是對ITask的InvokeAsync的包裝,使用try catch對InvokeAsync方法封裝為新的委托,當捕獲到符合條件的異常類型時,就返回某種結果。
以上可以解讀為,當遇到異常時,再重試請求,累計重試3次還是異常的話,處理為返回null值,期間總共最多請求了4次。
5.4 同步請求
HttpClient目前沒提供任何的同步請求方法,所以WebApiClient的請求也是一樣,如果遇到必須使用同步的場景,可以暫時使用 ITask.GetAwaiter().GetResult()方法等待結果。
相關內容:?
自動類型安全的REST .NET標準庫refit
WebApi client 的面向切面編程
net的retrofit--WebApiClient庫
原文地址:https://www.cnblogs.com/kewei/p/8302382.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的.net的retrofit--WebApiClient库深入篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用xUnit为.net core程序进
- 下一篇: .net的retrofit--WebAp