一.写在前面
适配器模式(Adapter)
可用来在现有接口和不兼容的类之间进行适配。有助于避免大规模改写现有客户代码,其工作机制是对现有类的接口进行包装,这样客户程序就能使用这个并非为其量身打造的类而又无需为此大动手术。 ----《JS设计模式》
将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
----《Head First设计模式》
这两本书中对适配器模式定义如此,适配器模式在多种设计模式当中属于比较容易理解的一种,其目的或者说可以解决的问题是新功能/新类型,不受原有类型/方法/功能的兼容,有了适配器这种巧妙地经验,我们可以保证对修改封闭,对拓展开放。而达到此目的,正需要面向接口,并保持职责的单一性。也许对C#开发者来说,见的最多的就是SqlDataAdapter。
二.认识UseWebApi
本文所涉及OWIN,.NetFramework,Webapi 开源源码下载地址为:
ht t p s : / /g i t h u b. c o m / as p n e t/ A s p N et K a ta n a
htt p s: / / g i th u b. c o m / AS P - N E T- M V C /a s p n e tw e b s t a ck
h t t p s : / /g i t h u b . c om / d o t ne t / c or e f x
熟悉OWIN体系的小伙伴们,一定都在Startup.cs中见过也使用过app.UseWebApi吧。app是IAppBuilder的对象
Startup.cs是OWIN katana实现的启动类,刚说的UseWebApi顾名思义,就是将WebApi作为一个OWIN中间件放在整个处理流程中。app是IAppBuilder的对象,其创建由IAppBuilderFactory负责。IAppBuilder定义了三个方法,分别为Build,New和Use. 这三个方法分别负责什么呢?
Build,返回OWIN管道入口点的一个实例,由 Microsoft.Owin.Host.SystemWeb中的Init方法调用。其返回实例将被转换为AppFun类型,AppFun( using AppFunc = Func<IDictionary<string, object>, Task>;)是什么呢?它是OWIN服务器与应用程序交互的应用程序委托,我们看到这个方法在OWIN.Host中调用,应该就能大概猜到个所以然。
New,用于返回一个AppBuilder实例,由IAppBuilderFactory调用并返回。
Use,就是我们在OWIN体系中,经常使用到的方法,我们可以定义自己的OWIN中间件,按照其定义规范,并Use到处理管道中,比如用户操作日志中间件,用户身份校验中间件等。
说到这里,我们应该很清晰的了解到WebApi是OWIN的一个中间件而已了吧。举个栗子:
1 public partial class Startup 2 { 3 4 public void Configuration(IAppBuilder app) 5 { 6 // This must happen FIRST otherwise CORS will not work. 7 // 引入OWin.Cors 解决跨域访问问题 8 app.UseCors(CorsOptions.AllowAll); 9 10 GlobalConfiguration.Configure(WebApiConfig.Register);11 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);12 13 ConfigureAuth(app);14 15 app.Use<UserContextMiddleware>();16 app.Use<UserOperationMiddleware>();17 app.UseWebApi(GlobalConfiguration.Configuration);18 }19 }
三.UseWebApi的实现
看到这里你一定会问,为什么IAppBuilder中没有定义UseWebapi方法呢,UseWebapi的实现在System.Web.Http.Owin的WebApiAppBuilderExtensions.cs中,UseWebApi是一个C# this拓展方法,和你所想到的答案并无差。在其实现中,调用了 builder.Use(typeof(HttpMessageHandlerAdapter), options);
到这里,一定要啰嗦几句不要怪我,Adapter的实现步骤:为了使一个类或者一个功能,兼容已有类/接口,那么
1.被适配器实现目标客户的接口或抽象方法,以便参数的传入
2.所实现接口/抽象类的方法中调用目标客户的方法
HttpMessageHandlerAdapter 这个主角终于出现了,对Adapter模式了解后的小伙伴们也一定能想得到,既然是HttpMessageHandler的Adapter,那么 在其类中 一定定义了一个private的字段,并且类型为HttpMessageHandler,你也一定能想得到这个Adapter继承了OwinMiddleware这个抽象类型并且实现其Invoke抽象方法,在HttpMessageHandlerAdapter的一个方法中一定调用了HttpMessageHandler的方法。那么通过源码我们了解到HttpMessageHandler的字段名为_messageHandler。(是不是和上面所说的Adapter实现方式类似呢,实现方式可能概括的不好,建议参阅更多文章和范例)
Asp.Net Webapi的消息处理管道是由HttpMessageHandler的委托链所组成的处理管道
HttpMessageHandler抽象类当中顶一个一个唯一的抽象方法用于实现,其入参为HttpRequestMessage,其出参为HttpResponseMessage。
1 protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
DelegatingHandler实现了HttpMessageHandler,其构造函数中传入HttpMessageHandler,并由同类对象innerHandler构成委托链。
推荐一篇MS文档 https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers,有兴趣可以稍微参照下。
1 protected DelegatingHandler(HttpMessageHandler innerHandler) 2 { 3 InnerHandler = innerHandler; 4 } 5 6 protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 7 { 8 if (request == null) 9 {10 throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest);11 }12 SetOperationStarted();13 return _innerHandler.SendAsync(request, cancellationToken);14 }
中间啰嗦了一串,为了说明HttpMessageHandler的作用,这样我们能进一步理解,为什么要有HttpMessageHandlerAdapter的存在,并在Use (WebApi中间件)的时候,将该类型传入。
在HttpMessageHandlerAdapter构造函数中,_messageHandler被包装为HttpMessageInvoker类型,这个类型的目的是提供一个专门的类,用于调用SendAsync方法。
刚才我们已经了解到HttpMessageHandlerAdapter实现了OWinMiddleware, 那么我们从源码中了解下,在其实现的抽象方法Invoke中,做了什么事情:其调用同类下的InvokeCore方法,InvokeCore中Create了HttpRequestMessage,并将其对象作为SendAsync的入参,最后得到HttpResponseMessage对象。