前言

本篇文章的内容是WebApiClient底层说明,也是WebApiClient系列接近尾声的一篇文章,如果你没有阅读过之前的的相关文章,可能会觉得本文章的内容断层,WebApiClient系列文章索引:

库简介

WebApiClient是开源在github上的一个httpClient客户端库,内部基于HttpClient开发,是一个只需要定义c#接口(interface),并打上相关特性,即可异步调用http-api的框架 ,支持.net framework4.5+、netcoreapp2.0和netstandard2.0。

WebApiClient是我2017年看到java的retrofit库之后,决心给.net打造的一款面向切面的httpclient客户端库,在开发过程中陆续发现.net下也有一些HttpClient包装库,WebApiClient库虽然是后起,却有其它库所没有很多优秀特征:

  • 原生的支持面向切面编程;
  • 内置丰富的特性,支持自定义特性;
  • 灵活和Filter、GlobalFilter和IParameterable;
  • 功能强大的序列化工具;
  • 与外部HttpClientHandler无缝衔接;
  • 独一无二的请求异常条件重试功能和异常处理链式语法功能

1. HttpRequestMessage简说

System.Net.Http.HttpRequestMessage表示一个请求消息,一般而言它包含一个完整的请求数据,主要由请求头和请求体组成,对于Post、Delete、Put等请求,其Content属性包含提交的数据体内容。

WebApiClient的ApiActionContex对象有个RequestMessage对象,是派生于HttpRequestMessage,同时实现了多个Addxxx方法用于给其属性Content对象添加数据内容。

2. HttpClientHandler简说

System.Net.Http.HttpClientHandler是一个与tcp层相关的对象,负责与远程服务器进行tcp连接,将HttpRequestMessage转换为http请求包发送给服务端,并等待服务端的响应。(以上这段话是我瞎说,仅供讨论)一般的网络相关配置的证书、代理和认证等在都在这里可以配置。

3. HttpClient简说

System.Net.Http.HttpClient必须与HttpClientHandler关联才能使用,一个HttpRequestMessage经过HttpClient之后,HttpClient的一些默认配置会影响到它,比如默认请求头等。HttpClient是使用关联的HttpClientHandler将HttpRequestMessage发送出去,也就是说,完全可以跳过HttpClient而使用HttpClientHandler来发送请求,方法是写一个类,继承于HttpClientHandler并公开一个方法,调用基类的SendAsync方法就可以发送请求。

4. WebApiClient库的HttpClient配置

WebApiClient库是对HttpClient的封装,所有的配置项在HttpApiConfig对象, 实例化HttpApiConfig对象时有个构造器可以传入IHttpClient的实例,而IHttpClient是对System.Net.Http.HttpClient的一个包装接口定义,WebApiClient.Defaults.HttpClient是对IHttpClient接口的一个实现,才下代码是WebApiCient与System.Net.Http.HttpClient的一个衔接:

IHttpClient client = new WebApiClient.Defaults.HttpClient();
var config = new WebApiClient.HttpApiConfig(client);

5. IHttpClient接口

5.1 IHttpClient的接口定义

/// <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); ...
}

5.2 IHttpClient的接口意图

IHttpClient接口意图将System.Net.Http.HttpClient实例和System.Net.Http.HttpClientHandler实例进行组合封装,隐藏底层的一些细节,同时描述了HttpClient和HttpClientHandler不可分割的关系,其默认实现对象WebApiClient.Defaults.HttpClient将System.Net.Http.HttpClient难用的几个功能也封装了一次:比如设置Cookie和设置代理等。

5.3 更换WebApiClient.Defaults.HttpClient关联的HttpClientHandler

一般而言,HttpClient没有多少扩展的价值,但HttpClientHandler就有很多扩展空间,其中System.Net.Http.WebRequestHandler也派生于HttpClientHandler,多了很一些配置的属性,很多时候,需要替换WebApiClient.Defaults.HttpClient的HttpClientHandler就可以,而不用从头实现IHttpClient接口,以下方式可以替换HttpClientHandler:

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);

6. 扩展JsonFormatter

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);

7. 扩展WebApiClient.Defaults.KeyValueFormatter

KeyValueFormatter基于Middleware思想,内部由多个转换器相连组成,随着转换器的增加,支持的类型也更多,KeyValueFormatter默认支持序列化以下类型:

  • 1、常用简单类型及其空类型(byte、int、short、long、doublue、flout、string、decimal、DateTime、Guid、enum、Version和Uri)
  • 2、支持IEnumerable递归拆解,默认最多16层
  • 3、KeyValuePair<,>的任意泛型
  • 4、多属性模型的第一层属性拆解

如果你需要支持更多的类型,需要派生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();
}
}
}

.net的retrofit--WebApiClient底层篇的更多相关文章

  1. 自己动手写插件底层篇—基于jquery移动插件实现

    序言 本章作为自己动手写插件的第一篇文章,会尽可能的详细描述一些实现的方式和预备知识的讲解,随着知识点积累的一点点深入,可能到了后期讲解也会有所跳跃.所以,希望知识点不是很扎实的读者或者是初学者,不要 ...

  2. 使用WebApiClient请求和管理Restful Api

    前言 本篇文章的内容是WebApiClient应用说明篇,如果你没有了解过WebApiClient,可以先阅读以下相关文章: WebApi client 的面向切面编程 我来给.Net设计一款Http ...

  3. [旧][Android] Retrofit 初步使用

    备注 原发表于2016.04.13,资料已过时,仅作备份,谨慎参考 Retrofit 是什么? Retrofit is a type-safe HTTP client for Android and ...

  4. Retrofit源码研究

    2016-05-06 15:35:27 最近抽空研究了一下Retrofit源码,包括API使用.源码结构.使用到的设计模式.SDK的架构设计.作者设计/实现思路等,会形成一系列文章. 以前Retrof ...

  5. 【Android - 框架】之OkHttp的使用

    OkHttp是一个非常优秀的网络访问框架,当下非常火的Retrofit的底层就是使用OkHttp进行封装的.接下来介绍以下OkHttp的简单使用. 1.导入依赖 在Android Studio中,在M ...

  6. Java 工程师成神之路 | 2019正式版

    本文为转载,原文见以下链接:https://mp.weixin.qq.com/s/4AMzq87V6eW3YPgE0mCdSw 1 基础篇 01 面向对象 → 什么是面向对象 面向对象.面向过程 面向 ...

  7. Android网络编程(一)HTTP协议原理

    相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection Android网络编程(三)Volley使用方法全解析 A ...

  8. Java进阶步骤

    一.基础篇 面向对象 什么是面向对象 面向对象.面向过程 面向对象的三大基本特征和五大基本原则 平台无关性 Java如何实现的平台无关 JVM还支持哪些语言(Kotlin.Groovy.JRuby.J ...

  9. 1. Retrofit2 -- Getting Started and Create an Android Client

    1. Retrofit2 -- Getting Started and Create an Android Client Retrofit tutorial 什么是 Retrofit 如何申明请求 准 ...

随机推荐

  1. 微信小程序——微信支付

    这个讲起来也就比较麻烦一点,因为需要的不仅仅是咱们代码上的技术,嘿嘿! 先整理一下思路.如果想做微信支付: 1.现有一个公司账户(非个人账户),并且实名认证过的. 2.微信号 必须开通微信支付功能. ...

  2. linux部署服务器遇到tomcat already start

    linux部署服务器遇到tomcat already start 前言,之前做了个汽车停车计费的后端,然后现在需要部署到服务器.正常部署,使用secureFx找到所属webapps目录,将文件上传.然 ...

  3. 对Java中堆栈的解析

    Java把内存分为两种:一种是栈内存,一种是堆内存 栈内存:在函数中定义的一些基本类型的变量和对象的引用变量,当超过变量的作用域之后,Java自动释放该变量内存 堆内存:存放new创建的对象和数组,由 ...

  4. 【java】获取当前环境属性及编码乱码示例

    package 字符编码; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; impo ...

  5. asp.net mvc 下拉列表

    第一步:新建一个格式化下拉列表的公共类文件 using System; using System.Collections; using System.Collections.Generic; usin ...

  6. iOS 类似2048、4096小游戏-OC

    大概思路(初步制作,粗工制造):demo 1.Collection 创建cell 2.cell上添加一个view,用来添加手势 3.字典用来存放数据->每次执行StarGame数组接收没有数字的 ...

  7. [置顶] Xamarin android中使用signalr实现即时通讯

    前面几天也写了一些signalr的例子,不过都是在Web端,今天我就来实践一下如何在xamarin android中使用signalr,刚好工作中也用到了这个,也算是总结一下学到的东西吧,希望能帮助你 ...

  8. [置顶] MVC中使用signalR入门教程

    一.前言:每次写总要说一点最近的感想 进入工作快半年了,昨天是最郁闷的一天,我怀疑我是不是得了"星期一综合征",每个星期一很没有状态.全身都有点酸痛,这个可能一个星期只有周末才打一 ...

  9. bzoj 3528: [Zjoi2014]星系调查

    Description 银河历59451年,在银河系有许许多多已被人类殖民的星系.如果想要在行 星系间往来,大家一般使用连接两个行星系的跳跃星门.  一个跳跃星门可以把 物质在它所连接的两个行星系中互 ...

  10. 如何严格设置php中session过期时间

    如何严格限制session在30分钟后过期! 1.设置客户端cookie的lifetime为30分钟: 2.设置session的最大存活周期也为30分钟: 3.为每个session值加入时间戳,然后在 ...