本篇文章的内容是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(); } } }