-
Notifications
You must be signed in to change notification settings - Fork 447
WebApiClient高级
WebApiClient将HttpClientHandler或SocketsHttpHandler实例包装为IHttpHanlder对象,提供统一的接口操作方式,对于这两个类型Handler,包装后IHttpHanlder的InnerHandler为被包装的对象的本身,即HttpClientHandler或SocketsHttpHandler;IHttpHanlder也可能包装了HttpClientHandler或SocketsHttpHandler的delegating包装对象,时些delegating对象会被拆解,直到得到内部的HttpClientHandler或SocketsHttpHandler,IHttpHanlder的InnerHandler为delegating handler。也就是说,不管是哪种类型的HttpMessageHandler,都可以被IHttpHanlder包装,提供的统一的设置属性,InnerHandler属性为原始传入的HttpMessageHandler。
WebApiClient将System.Net.Http.HttpClient和IHttpHanlder包装为IHttpClient对象,默认的实现类为WebApiClient.Defaults.HttpClient。 如果是外部的HttpMessageHandler实例,可以使用如下方式关联:
var config = new HttpApiConfig(handler);
var client = HttpApiClient.Create<IMyWebApi>(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 client = HttpApiClient.Create<IMyWebApi>(config);
如果你需要使用其它第三方的json序列化库,可以实现IJsonFormatter接口,然后实例化设置到HttpApiConfig的JsonFormatter属性。
WebApiClient.Defaults.KeyValueFormatter支持序列化以下类型:
- 常用简单类型及其空类型(byte、int、short、long、doublue、flout、string、decimal、DateTime、Guid、enum、Version和Uri)
- 支持IEnumerable递归拆解,默认最多16层
- KeyValuePair<,>的任意泛型
- 多属性模型的第一层属性拆解
如果你需要支持更多的类型,需要派生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();
}
}
}
ApiInterceptor是默认实现的IApiInterceptor,用于拦截http接口的调用,通过实现IApiInterceptor接口或派生WebApiClient.Defaults.ApiInterceptor,可以加入自己的拦截逻辑:
class MyApiInterceptor : WebApiClient.Defaults.ApiInterceptor
{
public MyApiInterceptor(HttpApiConfig config)
: base(config)
{
}
public override object Intercept(object target, MethodInfo method, object[] parameters)
{
Console.WriteLine($"正在请求方法{method.Name}");
return base.Intercept(target, method, parameters);
}
protected override ApiActionDescriptor GetApiActionDescriptor(MethodInfo method, object[] parameters)
{
return base.GetApiActionDescriptor(method, parameters);
}
}
var config = new HttpApiConfig();
var apiInerceptor = new MyApiInterceptor(config);
var client = HttpApiClient.Create(typeof(IMyWebApi), apiInerceptor) as IMyWebApi;
WebApiClient是不带业务性质的http客户端库,对于实际项目应用中,你可能会将服务器的一些响应情况,归类为对应的业务异常,常见的有种方式:
- 使用Http状态码来标记业务异常
- 统一返回200状态,在回复内容使用自定义业务状态码
不管是哪一种,客户端都需要根据不同的状态码执行一些业务规则逻辑,在WebApiClient使用中,可以将那些业务上任务异常的状态码转换为对应的自定义Exception,并抛出这些自定义Exception,将状态码转换为自定义的Exception,可以使用两种方式实现:
- 继承ApiActionFilterAttribute创建一个Filter,重写OnEndRequestAsync方法,根据Http状态码或业务状态码创建自定义业务Exception并抛出,然后将Filter应用到全局过滤器或修饰在接口或修饰在接口的方法上。
- 继承AutoReturnAttribute或ApiReturnAttribute,重写GetTaskResult方法,根据Http状态码或业务状态码创建自定义业务Exception并抛出,然后修饰在接口或修饰在接口的方法上。
使用ITask<>的Retry扩展方法捕获自定义业务Exception并实现重试机制,使用ITask<>的Handle()扩展方法捕获自定义业务Exception实现返回指定值。