从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式

Posted 蜗牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式相关的知识,希望对你有一定的参考价值。

本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws

一.写在前面

 适配器模式(Adapter)

可用来在现有接口和不兼容的类之间进行适配。有助于避免大规模改写现有客户代码,其工作机制是对现有类的接口进行包装,这样客户程序就能使用这个并非为其量身打造的类而又无需为此大动手术。                  ----《JS设计模式》

将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

                       ----《Head First设计模式》

这两本书中对适配器模式定义如此,适配器模式在多种设计模式当中属于比较容易理解的一种,其目的或者说可以解决的问题是新功能/新类型,不受原有类型/方法/功能的兼容,有了适配器这种巧妙地经验,我们可以保证对修改封闭,对拓展开放。而达到此目的,正需要面向接口,并保持职责的单一性。也许对C#开发者来说,见的最多的就是SqlDataAdapter。

                  

二.认识UseWebApi

本文所涉及OWIN,.NetFramework,Webapi 开源源码下载地址为:

https://github.com/aspnet/AspNetKatana

https://github.com/ASP-NET-MVC/aspnetwebstack

https://github.com/dotnet/corefx 

熟悉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模式了解后的小伙伴们也一定能想得到,既然是HttpMessageHandlerAdapter,那么 在其类中 一定定义了一个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对象。

四.写在最后

      就是这样,一次通过源码的阅读,再次对Adapter的理解,HttpMessageHandlerAdapter最终通过对OwinMiddleware的实现,在Invoke中通过HttpMessageInvoker调用执行SendAsync,丢入HttpRequestMessage,拿到ResponseMessage.最终附上HttpMessageHandlerAdapter源码,更多源码,还是从第二段的连接中下载吧。

  1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2 
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.Diagnostics.CodeAnalysis;
  6 using System.Diagnostics.Contracts;
  7 using System.IO;
  8 using System.Net;
  9 using System.Net.Http;
 10 using System.Net.Http.Headers;
 11 using System.Runtime.ExceptionServices;
 12 using System.Security.Principal;
 13 using System.Threading;
 14 using System.Threading.Tasks;
 15 using System.Web.Http.Controllers;
 16 using System.Web.Http.ExceptionHandling;
 17 using System.Web.Http.Hosting;
 18 using System.Web.Http.Owin.ExceptionHandling;
 19 using System.Web.Http.Owin.Properties;
 20 using Microsoft.Owin;
 21 
 22 namespace System.Web.Http.Owin
 23 {
 24     /// <summary>
 25     /// Represents an OWIN component that submits requests to an <see cref="HttpMessageHandler"/> when invoked.
 26     /// </summary>
 27     public class HttpMessageHandlerAdapter : OwinMiddleware, IDisposable
 28     {
 29         private readonly HttpMessageHandler _messageHandler;
 30         private readonly HttpMessageInvoker _messageInvoker;
 31         private readonly IHostBufferPolicySelector _bufferPolicySelector;
 32         private readonly IExceptionLogger _exceptionLogger;
 33         private readonly IExceptionHandler _exceptionHandler;
 34         private readonly CancellationToken _appDisposing;
 35 
 36         private bool _disposed;
 37 
 38         /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
 39         /// <param name="next">The next component in the pipeline.</param>
 40         /// <param name="options">The options to configure this adapter.</param>
 41         public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandlerOptions options)
 42             : base(next)
 43         {
 44             if (options == null)
 45             {
 46                 throw new ArgumentNullException("options");
 47             }
 48 
 49             _messageHandler = options.MessageHandler;
 50 
 51             if (_messageHandler == null)
 52             {
 53                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 54                     typeof(HttpMessageHandlerOptions).Name, "MessageHandler"), "options");
 55             }
 56 
 57             _messageInvoker = new HttpMessageInvoker(_messageHandler);
 58             _bufferPolicySelector = options.BufferPolicySelector;
 59 
 60             if (_bufferPolicySelector == null)
 61             {
 62                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 63                     typeof(HttpMessageHandlerOptions).Name, "BufferPolicySelector"), "options");
 64             }
 65 
 66             _exceptionLogger = options.ExceptionLogger;
 67 
 68             if (_exceptionLogger == null)
 69             {
 70                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 71                     typeof(HttpMessageHandlerOptions).Name, "ExceptionLogger"), "options");
 72             }
 73 
 74             _exceptionHandler = options.ExceptionHandler;
 75 
 76             if (_exceptionHandler == null)
 77             {
 78                 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
 79                     typeof(HttpMessageHandlerOptions).Name, "ExceptionHandler"), "options");
 80             }
 81 
 82             _appDisposing = options.AppDisposing;
 83 
 84             if (_appDisposing.CanBeCanceled)
 85             {
 86                 _appDisposing.Register(OnAppDisposing);
 87             }
 88         }
 89 
 90         /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
 91         /// <param name="next">The next component in the pipeline.</param>
 92         /// <param name="messageHandler">The <see cref="HttpMessageHandler"/> to submit requests to.</param>
 93         /// <param name="bufferPolicySelector">
 94         /// The <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
 95         /// responses.
 96         /// </param>
 97         /// <remarks>
 98         /// This constructor is retained for backwards compatibility. The constructor taking
 99         /// <see cref="HttpMessageHandlerOptions"/> should be used instead.
100         /// </remarks>
101         [Obsolete("Use the HttpMessageHandlerAdapter(OwinMiddleware, HttpMessageHandlerOptions) constructor instead.")]
102         public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandler messageHandler,
103             IHostBufferPolicySelector bufferPolicySelector)
104             : this(next, CreateOptions(messageHandler, bufferPolicySelector))
105         {
106         }
107 
108         /// <summary>Gets the <see cref="HttpMessageHandler"/> to submit requests to.</summary>
109         public HttpMessageHandler MessageHandler
110         {
111             get { return _messageHandler; }
112         }
113 
114         /// <summary>
115         /// Gets the <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
116         /// responses.
117         /// </summary>
118         public IHostBufferPolicySelector BufferPolicySelector
119         {
120             get { return _bufferPolicySelector; }
121         }
122 
123         /// <summary>Gets the <see cref="IExceptionLogger"/> to use to log unhandled exceptions.</summary>
124         public IExceptionLogger ExceptionLogger
125         {
126             get { return _exceptionLogger; }
127         }
128 
129         /// <summary>Gets the <see cref="IExceptionHandler"/> to use to process unhandled exceptions.</summary>
130         public IExceptionHandler ExceptionHandler
131         {
132             get { return _exceptionHandler; }
133         }
134 
135         /// <summary>Gets the <see cref="CancellationToken"/> that triggers cleanup of this component.</summary>
136         public CancellationToken AppDisposing
137         {
138             get { return _appDisposing; }
139         }
140 
141         /// <inheritdoc />
142         public override Task Invoke(IOwinContext context)
143         {
144             if (context == null)
145             {
146                 throw new ArgumentNullException("context");
147             }
148 
149             IOwinRequest owinRequest = context.Request;
150             IOwinResponse owinResponse = context.Response;
151 
152             if (owinRequest == null)
153             {
154                 throw Error.InvalidOperation(OwinResources.OwinContext_NullRequest);
155             }
156             if (owinResponse == null)
157             {
158                 throw Error.InvalidOperation(OwinResources.OwinContext_NullResponse);
159             }
160 
161             return InvokeCore(context, owinRequest, owinResponse);
162         }
163 
164         private async Task InvokeCore(IOwinContext context, IOwinRequest owinRequest, IOwinResponse owinResponse)
165         {
166             CancellationToken cancellationToken = owinRequest.CallCancelled;
167             HttpContent requestContent;
168 
169             bool bufferInput = _bufferPolicySelector.UseBufferedInputStream(hostContext: context);
170 
171             if (!bufferInput)
172             {
173                 owinRequest.DisableBuffering();
174             }
175 
176             if (!owinRequest.Body.CanSeek && bufferInput)
177             {
178                 requestContent = await CreateBufferedRequestContentAsync(owinRequest, cancellationToken);
179             }
180             else
181             {
182                 requestContent = CreateStreamedRequestContent(owinRequest);
183             }
184 
185             HttpRequestMessage request = CreateRequestMessage(owinRequest, requestContent);
186             MapRequestProperties(request, context);
187 
188             SetPrincipal(owinRequest.User);
189 
190             HttpResponseMessage response = null;
191             bool callNext;
192 
193             try
194             {
195                 response = await _messageInvoker.SendAsync(request, cancellationToken);
196 
197                 // Handle null responses
198                 if (response == null)
199                 {
200                     throw Error.InvalidOperation(OwinResources.SendAsync_ReturnedNull);
201                 }
202 
203                 // Handle soft 404s where no route matched - call the next component
204                 if (IsSoftNotFound(request, response))
205                 {
206                     callNext = true;
207                 }
208                 else
209                 {
210                     callNext = false;
211 
212                     // Compute Content-Length before calling UseBufferedOutputStream because the default implementation
213                     // accesses that header and we want to catch any exceptions calling TryComputeLength here.
214 
215                     if (response.Content == null
216                         || await ComputeContentLengthAsync(request, response, owinResponse, cancellationToken))
217                     {
218                         bool bufferOutput = _bufferPolicySelector.UseBufferedOutputStream(response);
219 
220                         if (!bufferOutput)
221                         {
222                             owinResponse.DisableBuffering();
223                         }
224                         else if (response.Content != null)
225                         {
226                             response = await BufferResponseContentAsync(request, response, cancellationToken);
227                         }
228 
229                         if (await PrepareHeadersAsync(request, response, owinResponse, cancellationToken))
230                         {
231                             await SendResponseMessageAsync(request, response, owinResponse, cancellationToken);
232                         }
233                     }
234                 }
235             }
236             finally
237             {
238                 request.DisposeRequestResources();
239                 request.Dispose();
240                 if (response != null)
241                 {
242                     response.Dispose();
243                 }
244             }
245 
246             // Call the next component if no route matched
247             if (callNext && Next != null)
248             {
249                 await Next.Invoke(context);
250             }
251         }
252 
253         private static HttpContent CreateStreamedRequestContent(IOwinRequest owinRequest)
254         {
255             // Note that we must NOT dispose owinRequest.Body in this case. Disposing it would close the input
256             // stream and prevent cascaded components from accessing it. The server MUST handle any necessary
257             // cleanup upon request completion. NonOwnedStream prevents StreamContent (or its callers including
258             // HttpRequestMessage) from calling Close or Dispose on owinRequest.Body.
259             return new StreamContent(new NonOwnedStream(owinRequest.Body));
260         }
261 
262         private static async Task<HttpContent> CreateBufferedRequestContentAsync(IOwinRequest owinRequest,
263             CancellationToken cancellationToken)
264         {
265             // We need to replace the request body with a buffered stream so that other components can read the stream.
266             // For this stream to be useful, it must NOT be diposed along with the request. Streams created by
267             // StreamContent do get disposed along with the request, so use MemoryStream to buffer separately.
268             MemoryStream buffer;
269             int? contentLength = owinRequest.GetContentLength();
270 
271             if (!contentLength.HasValue)
272             {
273                 buffer = new MemoryStream();
274             }
275             else
276             {
277                 buffer = new MemoryStream(contentLength.Value);
278             }
279 
280             cancellationToken.ThrowIfCancellationRequested();
281 
282             using (StreamContent copier = new StreamContent(owinRequest.Body))
283             {
284                 await copier.CopyToAsync(buffer);
285             }
286 
287             // Provide the non-disposing, buffered stream to later OWIN components (set to the stream\'s beginning).
288             buffer.Position = 0;
289             owinRequest.Body = buffer;
290 
291             // For MemoryStream, Length is guaranteed to be an int.
292             return new ByteArrayContent(buffer.GetBuffer(), 0, (int)buffer.Length);
293         }
294 
295         private static HttpRequestMessage CreateR

以上是关于从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式的主要内容,如果未能解决你的问题,请参考以下文章

寻找解决策略:无法加载文件或程序集[重复]

Htt的C++教程丨3

Azure api OAuth2 隐式流适用于 http 但不适用于 htt

java 示例登录Android上的Google登录活动,该活动检索用于服务器端身份验证的授权代码。见htt

Confluence 6 通过 SSL 或 HTTPS 运行 - 重定向所有的 URLS 到 HTT

HTTPS从入门到了解