本篇文章的內容是WebApiClient底層說明,也是WebApiClient系列接近尾聲的一篇文章,若是你沒有閱讀過以前的的相關文章,可能會以爲本文章的內容斷層,WebApiClient系列文章索引:html
WebApiClient是開源在github上的一個httpClient客戶端庫,內部基於HttpClient開發,是一個只須要定義c#接口(interface),並打上相關特性,便可異步調用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。
WebApiClient是我2017年看到java的retrofit庫以後,決心給.net打造的一款面向切面的httpclient客戶端庫,在開發過程當中陸續發現.net下也有一些HttpClient包裝庫,WebApiClient庫雖然是後起,卻有其它庫所沒有不少優秀特徵:java
System.Net.Http.HttpRequestMessage表示一個請求消息,通常而言它包含一個完整的請求數據,主要由請求頭和請求體組成,對於Post、Delete、Put等請求,其Content屬性包含提交的數據體內容。git
WebApiClient的ApiActionContex對象有個RequestMessage對象,是派生於HttpRequestMessage,同時實現了多個Addxxx方法用於給其屬性Content對象添加數據內容。github
System.Net.Http.HttpClientHandler是一個與tcp層相關的對象,負責與遠程服務器進行tcp鏈接,將HttpRequestMessage轉換爲http請求包發送給服務端,並等待服務端的響應。(以上這段話是我瞎說,僅供討論)通常的網絡相關配置的證書、代理和認證等在都在這裏能夠配置。web
System.Net.Http.HttpClient必須與HttpClientHandler關聯才能使用,一個HttpRequestMessage通過HttpClient以後,HttpClient的一些默認配置會影響到它,好比默認請求頭等。HttpClient是使用關聯的HttpClientHandler將HttpRequestMessage發送出去,也就是說,徹底能夠跳過HttpClient而使用HttpClientHandler來發送請求,方法是寫一個類,繼承於HttpClientHandler並公開一個方法,調用基類的SendAsync方法就能夠發送請求。編程
WebApiClient庫是對HttpClient的封裝,全部的配置項在HttpApiConfig對象, 實例化HttpApiConfig對象時有個構造器能夠傳入IHttpClient的實例,而IHttpClient是對System.Net.Http.HttpClient的一個包裝接口定義,WebApiClient.Defaults.HttpClient是對IHttpClient接口的一個實現,才下代碼是WebApiCient與System.Net.Http.HttpClient的一個銜接:json
IHttpClient client = new WebApiClient.Defaults.HttpClient(); var config = new WebApiClient.HttpApiConfig(client);
/// <summary> /// 定義HttpClient的接口 /// </summary> public interface IHttpClient : IDisposable { /// <summary> /// 獲取關聯的Http處理對象 /// </summary> HttpClientHandler Handler { get; } /// <summary> /// 獲取默認的請求頭管理對象 /// </summary> HttpRequestHeaders DefaultRequestHeaders { get; } /// <summary> /// 異步發送請求 /// </summary> /// <param name="request">請求消息</param> /// <returns></returns> Task<HttpResponseMessage> SendAsync(HttpApiRequestMessage request); ... }
IHttpClient接口意圖將System.Net.Http.HttpClient實例和System.Net.Http.HttpClientHandler實例進行組合封裝,隱藏底層的一些細節,同時描述了HttpClient和HttpClientHandler不可分割的關係,其默認實現對象WebApiClient.Defaults.HttpClient將System.Net.Http.HttpClient難用的幾個功能也封裝了一次:好比設置Cookie和設置代理等。c#
通常而言,HttpClient沒有多少擴展的價值,但HttpClientHandler就有不少擴展空間,其中System.Net.Http.WebRequestHandler也派生於HttpClientHandler,多了很一些配置的屬性,不少時候,須要替換WebApiClient.Defaults.HttpClient的HttpClientHandler就能夠,而不用從頭實現IHttpClient接口,如下方式能夠替換HttpClientHandler:api
class MyHttpClient : WebApiClient.Defaults.HttpClient { protected override HttpClientHandler CreateHttpClientHandler() { // or return your handler return new WebRequestHandler(); } } var config = new HttpApiConfig(new MyHttpClient()); var myWebApi = HttpApiClient.Create(config);
若是是外部的HttpClientHandler實例,可使用以下方式關聯:服務器
var client = new WebApiClient.Defaults.HttpClient(handler); var config = new HttpApiConfig(client); var myWebApi = HttpApiClient.Create(config);
WebApiClient.Defaults.JsonFormatter使用了json.net,每次序列化或反序列化時都會建立JsonSerializerSettings,能夠派生WebApiClient.Defaults.JsonFormatter返回自定義的JsonSerializerSettings:
class MyJsonFormatter : WebApiClient.Defaults.JsonFormatter { protected override JsonSerializerSettings CreateSerializerSettings() { return new JsonSerializerSettings { // your setting }; } } var config = new HttpApiConfig { JsonFormatter = new MyJsonFormatter() }; var myWebApi = HttpApiClient.Create(config);
KeyValueFormatter基於Middleware思想,內部由多個轉換器相連組成,隨着轉換器的增長,支持的類型也更多,KeyValueFormatter默認支持序列化如下類型:
若是你須要支持更多的類型,須要派生KeyValueFormatter增長功能:
class MyKeValueFormatter : WebApiClient.Defaults.KeyValueFormatter { protected override IEnumerable<ConverterBase> GetConverters() { // 在原有轉換器以前插入DynamicObjectConverter var addin = new[] { new DynamicObjectConverter() }; return addin.Concat(base.GetConverters()); } } /// <summary> /// 表示動態類型轉換器 /// </summary> class DynamicObjectConverter : ConverterBase { /// <summary> /// 執行轉換 /// </summary> /// <param name="context">轉換上下文</param> /// <returns></returns> public override IEnumerable<KeyValuePair<string, string>> Invoke(ConvertContext context) { var dynamicObject = context.Data as DynamicObject; if (dynamicObject != null) { return from name in dynamicObject.GetDynamicMemberNames() let value = this.GetValue(dynamicObject, name) let ctx = new ConvertContext(name, value, context.Depths, context.Options) select ctx.ToKeyValuePair(); } return this.Next.Invoke(context); } /// <summary> /// 獲取動態類型的值 /// </summary> /// <param name="dynamicObject">實例</param> /// <param name="name">名稱</param> /// <returns></returns> private object GetValue(DynamicObject dynamicObject, string name) { object value; var binder = new MemberBinder(name); dynamicObject.TryGetMember(binder, out value); return value; } /// <summary> /// 表示成員值的獲取綁定 /// </summary> private class MemberBinder : GetMemberBinder { /// <summary> /// 鍵的信息獲取綁定 /// </summary> /// <param name="key">鍵名</param> public MemberBinder(string key) : base(key, false) { } /// <summary> /// 在派生類中重寫時,若是沒法綁定目標動態對象,則執行動態獲取成員操做的綁定 /// </summary> /// <param name="target"></param> /// <param name="errorSuggestion"></param> /// <returns></returns> public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { throw new NotImplementedException(); } } }