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

.mytitle { background: #2B6695; color: white; font-family: "微软雅黑", "宋体", "黑体", Arial; font-size: 18px; font-weight: bold; height: 25px; line-height: 25px; margin: 15px 0 !important; padding: 5px 0 5px 20px; width: 97% }
.articleFooter { padding: 15px; background-color: #FFF7DC; border: 2px solid #FFBFBF }

本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 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体系的小伙伴们,一定都在Startip.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 CreateRequestMessage(IOwinRequest owinRequest, HttpContent requestContent)
296         {
297             // Create the request
298             HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(owinRequest.Method), owinRequest.Uri);
299
300             try
301             {
302                 // Set the body
303                 request.Content = requestContent;
304
305                 // Copy the headers
306                 foreach (KeyValuePair<string, string[]> header in owinRequest.Headers)
307                 {
308                     if (!request.Headers.TryAddWithoutValidation(header.Key, header.Value))
309                     {
310                         bool success = requestContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
311                         Contract.Assert(success,
312                             "Every header can be added either to the request headers or to the content headers");
313                     }
314                 }
315             }
316             catch
317             {
318                 request.Dispose();
319                 throw;
320             }
321
322             return request;
323         }
324
325         private static void MapRequestProperties(HttpRequestMessage request, IOwinContext context)
326         {
327             // Set the OWIN context on the request
328             request.SetOwinContext(context);
329
330             // Set a request context on the request that lazily populates each property.
331             HttpRequestContext requestContext = new OwinHttpRequestContext(context, request);
332             request.SetRequestContext(requestContext);
333         }
334
335         private static void SetPrincipal(IPrincipal user)
336         {
337             if (user != null)
338             {
339                 Thread.CurrentPrincipal = user;
340             }
341         }
342
343         private static bool IsSoftNotFound(HttpRequestMessage request, HttpResponseMessage response)
344         {
345             if (response.StatusCode == HttpStatusCode.NotFound)
346             {
347                 bool routingFailure;
348                 if (request.Properties.TryGetValue<bool>(HttpPropertyKeys.NoRouteMatched, out routingFailure)
349                     && routingFailure)
350                 {
351                     return true;
352                 }
353             }
354             return false;
355         }
356
357         private async Task<HttpResponseMessage> BufferResponseContentAsync(HttpRequestMessage request,
358             HttpResponseMessage response, CancellationToken cancellationToken)
359         {
360             ExceptionDispatchInfo exceptionInfo;
361
362             cancellationToken.ThrowIfCancellationRequested();
363
364             try
365             {
366                 await response.Content.LoadIntoBufferAsync();
367                 return response;
368             }
369             catch (OperationCanceledException)
370             {
371                 // Propogate the canceled task without calling exception loggers or handlers.
372                 throw;
373             }
374             catch (Exception exception)
375             {
376                 exceptionInfo = ExceptionDispatchInfo.Capture(exception);
377             }
378
379             // If the content can‘t be buffered, create a buffered error response for the exception
380             // This code will commonly run when a formatter throws during the process of serialization
381
382             Debug.Assert(exceptionInfo.SourceException != null);
383
384             ExceptionContext exceptionContext = new ExceptionContext(exceptionInfo.SourceException,
385                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferContent, request, response);
386
387             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
388             HttpResponseMessage errorResponse = await _exceptionHandler.HandleAsync(exceptionContext,
389                 cancellationToken);
390
391             response.Dispose();
392
393             if (errorResponse == null)
394             {
395                 exceptionInfo.Throw();
396                 return null;
397             }
398
399             // We have an error response to try to buffer and send back.
400
401             response = errorResponse;
402             cancellationToken.ThrowIfCancellationRequested();
403
404             Exception errorException;
405
406             try
407             {
408                 // Try to buffer the error response and send it back.
409                 await response.Content.LoadIntoBufferAsync();
410                 return response;
411             }
412             catch (OperationCanceledException)
413             {
414                 // Propogate the canceled task without calling exception loggers.
415                 throw;
416             }
417             catch (Exception exception)
418             {
419                 errorException = exception;
420             }
421
422             // We tried to send back an error response with content, but we couldn‘t. It‘s an edge case; the best we
423             // can do is to log that exception and send back an empty 500.
424
425             ExceptionContext errorExceptionContext = new ExceptionContext(errorException,
426                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferError, request, response);
427             await _exceptionLogger.LogAsync(errorExceptionContext, cancellationToken);
428
429             response.Dispose();
430             return request.CreateResponse(HttpStatusCode.InternalServerError);
431         }
432
433         // Prepares Content-Length and Transfer-Encoding headers.
434         private Task<bool> PrepareHeadersAsync(HttpRequestMessage request, HttpResponseMessage response,
435             IOwinResponse owinResponse, CancellationToken cancellationToken)
436         {
437             Contract.Assert(response != null);
438             HttpResponseHeaders responseHeaders = response.Headers;
439             Contract.Assert(responseHeaders != null);
440             HttpContent content = response.Content;
441             bool isTransferEncodingChunked = responseHeaders.TransferEncodingChunked == true;
442             HttpHeaderValueCollection<TransferCodingHeaderValue> transferEncoding = responseHeaders.TransferEncoding;
443
444             if (content != null)
445             {
446                 HttpContentHeaders contentHeaders = content.Headers;
447                 Contract.Assert(contentHeaders != null);
448
449                 if (isTransferEncodingChunked)
450                 {
451                     // According to section 4.4 of the HTTP 1.1 spec, HTTP responses that use chunked transfer
452                     // encoding must not have a content length set. Chunked should take precedence over content
453                     // length in this case because chunked is always set explicitly by users while the Content-Length
454                     // header can be added implicitly by System.Net.Http.
455                     contentHeaders.ContentLength = null;
456                 }
457                 else
458                 {
459                     // Copy the response content headers only after ensuring they are complete.
460                     // We ask for Content-Length first because HttpContent lazily computes this header and only
461                     // afterwards writes the value into the content headers.
462                     return ComputeContentLengthAsync(request, response, owinResponse, cancellationToken);
463                 }
464             }
465
466             // Ignore the Transfer-Encoding header if it is just "chunked"; the host will likely provide it when no
467             // Content-Length is present (and if the host does not, there‘s not much better this code could do to
468             // transmit the current response, since HttpContent is assumed to be unframed; in that case, silently drop
469             // the Transfer-Encoding: chunked header).
470             // HttpClient sets this header when it receives chunked content, but HttpContent does not include the
471             // frames. The OWIN contract is to set this header only when writing chunked frames to the stream.
472             // A Web API caller who desires custom framing would need to do a different Transfer-Encoding (such as
473             // "identity, chunked").
474             if (isTransferEncodingChunked && transferEncoding.Count == 1)
475             {
476                 transferEncoding.Clear();
477             }
478
479             return Task.FromResult(true);
480         }
481
482         [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "unused",
483             Justification = "unused variable necessary to call getter")]
484         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
485             Justification = "Exception is turned into an error response.")]
486         private Task<bool> ComputeContentLengthAsync(HttpRequestMessage request, HttpResponseMessage response,
487             IOwinResponse owinResponse, CancellationToken cancellationToken)
488         {
489             Contract.Assert(response != null);
490             HttpResponseHeaders responseHeaders = response.Headers;
491             Contract.Assert(responseHeaders != null);
492             HttpContent content = response.Content;
493             Contract.Assert(content != null);
494             HttpContentHeaders contentHeaders = content.Headers;
495             Contract.Assert(contentHeaders != null);
496
497             Exception exception;
498
499             try
500             {
501                 var unused = contentHeaders.ContentLength;
502
503                 return Task.FromResult(true);
504             }
505             catch (Exception ex)
506             {
507                 exception = ex;
508             }
509
510             return HandleTryComputeLengthExceptionAsync(exception, request, response, owinResponse, cancellationToken);
511         }
512
513         private async Task<bool> HandleTryComputeLengthExceptionAsync(Exception exception, HttpRequestMessage request,
514             HttpResponseMessage response, IOwinResponse owinResponse, CancellationToken cancellationToken)
515         {
516             Contract.Assert(owinResponse != null);
517
518             ExceptionContext exceptionContext = new ExceptionContext(exception,
519                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterComputeContentLength, request, response);
520             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
521
522             // Send back an empty error response if TryComputeLength throws.
523             owinResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
524             SetHeadersForEmptyResponse(owinResponse.Headers);
525             return false;
526         }
527
528         private Task SendResponseMessageAsync(HttpRequestMessage request, HttpResponseMessage response,
529             IOwinResponse owinResponse, CancellationToken cancellationToken)
530         {
531             owinResponse.StatusCode = (int)response.StatusCode;
532             owinResponse.ReasonPhrase = response.ReasonPhrase;
533
534             // Copy non-content headers
535             IDictionary<string, string[]> responseHeaders = owinResponse.Headers;
536             foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
537             {
538                 responseHeaders[header.Key] = header.Value.AsArray();
539             }
540
541             HttpContent responseContent = response.Content;
542             if (responseContent == null)
543             {
544                 SetHeadersForEmptyResponse(responseHeaders);
545                 return TaskHelpers.Completed();
546             }
547             else
548             {
549                 // Copy content headers
550                 foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in responseContent.Headers)
551                 {
552                     responseHeaders[contentHeader.Key] = contentHeader.Value.AsArray();
553                 }
554
555                 // Copy body
556                 return SendResponseContentAsync(request, response, owinResponse, cancellationToken);
557             }
558         }
559
560         private static void SetHeadersForEmptyResponse(IDictionary<string, string[]> headers)
561         {
562             // Set the content-length to 0 to prevent the server from sending back the response chunked
563             headers["Content-Length"] = new string[] { "0" };
564         }
565
566         private async Task SendResponseContentAsync(HttpRequestMessage request, HttpResponseMessage response,
567             IOwinResponse owinResponse, CancellationToken cancellationToken)
568         {
569             Contract.Assert(response != null);
570             Contract.Assert(response.Content != null);
571
572             Exception exception;
573             cancellationToken.ThrowIfCancellationRequested();
574
575             try
576             {
577                 await response.Content.CopyToAsync(owinResponse.Body);
578                 return;
579             }
580             catch (OperationCanceledException)
581             {
582                 // Propogate the canceled task without calling exception loggers;
583                 throw;
584             }
585             catch (Exception ex)
586             {
587                 exception = ex;
588             }
589
590             // We‘re streaming content, so we can only call loggers, not handlers, as we‘ve already (possibly) send the
591             // status code and headers across the wire. Log the exception, but then just abort.
592             ExceptionContext exceptionContext = new ExceptionContext(exception,
593                 OwinExceptionCatchBlocks.HttpMessageHandlerAdapterStreamContent, request, response);
594             await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
595             await AbortResponseAsync();
596         }
597
598         private static Task AbortResponseAsync()
599         {
600             // OWIN doesn‘t yet support an explicit Abort event. Returning a canceled task is the best contract at the
601             // moment.
602             return TaskHelpers.Canceled();
603         }
604
605         // Provides HttpMessageHandlerOptions for callers using the old constructor.
606         private static HttpMessageHandlerOptions CreateOptions(HttpMessageHandler messageHandler,
607             IHostBufferPolicySelector bufferPolicySelector)
608         {
609             if (messageHandler == null)
610             {
611                 throw new ArgumentNullException("messageHandler");
612             }
613
614             if (bufferPolicySelector == null)
615             {
616                 throw new ArgumentNullException("bufferPolicySelector");
617             }
618
619             // Callers using the old constructor get the default exception handler, no exception logging support, and no
620             // app cleanup support.
621
622             return new HttpMessageHandlerOptions
623             {
624                 MessageHandler = messageHandler,
625                 BufferPolicySelector = bufferPolicySelector,
626                 ExceptionLogger = new EmptyExceptionLogger(),
627                 ExceptionHandler = new DefaultExceptionHandler(),
628                 AppDisposing = CancellationToken.None
629             };
630         }
631
632         /// <summary>
633         /// Releases unmanaged and optionally managed resources.
634         /// </summary>
635         /// <param name="disposing">
636         /// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release
637         /// only unmanaged resources.
638         /// </param>
639         /// <remarks>
640         /// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
641         /// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
642         /// <see cref="HttpMessageHandlerOptions"/>.
643         /// </remarks>
644         protected virtual void Dispose(bool disposing)
645         {
646             if (disposing)
647             {
648                 OnAppDisposing();
649             }
650         }
651
652         /// <inheritdoc />
653         /// <remarks>
654         /// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
655         /// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
656         /// <see cref="HttpMessageHandlerOptions"/>.
657         /// </remarks>
658         public void Dispose()
659         {
660             Dispose(true);
661             GC.SuppressFinalize(this);
662         }
663
664         private void OnAppDisposing()
665         {
666             if (!_disposed)
667             {
668                 _messageInvoker.Dispose();
669                 _disposed = true;
670             }
671         }
672     }
673 }

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下加【推荐】按钮。

如果,您希望更容易地发现我的新博客,不妨点击下方红色【关注】的。

因为,我的分享热情也离不开您的肯定支持。

感谢您的阅读,我将持续输出分享,我是蜗牛, 保持学习,谨记谦虚。不端不装,有趣有梦。

时间: 2024-08-05 11:38:28

从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式的相关文章

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

一.写在前面 适配器模式(Adapter) 可用来在现有接口和不兼容的类之间进行适配.有助于避免大规模改写现有客户代码,其工作机制是对现有类的接口进行包装,这样客户程序就能使用这个并非为其量身打造的类而又无需为此大动手术.   ----<JS设计模式> 将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间.  ----<Head First设计模式> 这两本书中对适配器模式定义如此,适配器模式在多种设计模式当中属于比较容易理解的一种,其目的或者说可以解

慎用System.Web.HttpContext.Current

每当控制流离开页面派生的Web表单上的代码的时候,HttpContext类的静态属性Current可能是有用的. 使用这个属性,我们可以获取当前请求(Request),响应(Response),会话(Session,)和应用程序对象(Application objects)以及请求更多服务. 以下面的代码为例. private void Page_Load(object sender, System.EventArgs e) { MyClass myClass = new MyClass();

命名空间“System.Web.Mvc”中不存在类型或命名空间“Ajax”(是否缺少程序集引用?)

好吧,非常激动的说,这个问题搞了我一个晚上,网上的帖子太少了,好像不超过2篇,而且说得不是很明白,最后看了一个外国的一个类似于 论坛的网站(叫:Stack Flow),结果还是让我给解决了,虽然这个点了,但是十分激动,熬夜也要记录下这一东西. 在asp.net mvc中,需要添加对System.Web.Mvc的引用,但是添加引用了,还是会出现这个问题,出现这个问题的原因是什么呢,我猜是 因为你当前的这个项目是从别的电脑拷过来,想研究别人的代码,在别人电脑明明运行得好好的,但是到自己电脑怎么会出问

System.Web.Routing入门及进阶 上篇

System.Web.Routing已经作为一个程序集包含在.net3.5sp1中发布了.虽然我们并没有在3.5sp1中发现Asp.net Mvc的踪迹,但是亦以感觉到它离我们不远了. System.Web.Routing用于在Asp.net的Web应用程序中进行URLRouting. 所谓UrlRouting就是将一个地址映射为另一个地址,比如我访问/chsword/2008/08/27.html其实是访问了/chsword/article.aspx?y=2008&m=08&d=27这个

System.Web.Caching

System.Web.Caching 本文转载自 宋辉 缓存主要是为了提高数据的读取速度.因为服务器和应用客户端之间存在着流量的瓶颈,所以读取大容量数据时,使用缓存来直接为客户端服务,可以减少客户端与服务器端的数据交互,从而大大提高程序的性能. 本章从缓存所在的命名空间“System.Web.Caching”开始,详细介绍框架提供的缓存类和操作方法,主要涉及简单数据的缓存.数据缓存依赖和数据库缓存依赖三个技术要点,最后演示一个完全使用缓存实现数据读取的实例. 16.1  System.Web.C

设置项目中控件的样式:webconfig中system.web下的Pages节点

关于system.web下面的Pages节点的详细说明,MSDN上面有,看这里. 这个Pages节点下的theme属性可以设置项目中控件的样式,直接引用skin文件来设置样式,不用在每个页面中添加,可以保证页面风格的一致性: <pages theme= "defaultTheme" /> 关于theme的添加: 创建主题完毕之后,在App_Themes文件夹下创建自己的样式,pages里面就可以直接使用了. 当然,如果要在某个页面中添加自定义的样式,可以在aspx中指定pa

(asp.net MVC学习)System.Web.Mvc.HtmlHelper学习及使用

在ASP.NET MVC框架中没有了自己的控件,页面显示完全就回到了写html代码的年代.还好在asp.net mvc框架中也有自带的HtmlHelper和UrlHelper两个帮助类.另外在MvcContrib扩展项目中也有扩展一些帮助类,这样我们就不光 只能使用完整的html来编写了需要显示的页面了,就可以使用这些帮助类来完成,但最后运行时都还是要生成html代码的. 先来看看HtmlHelper能帮我们生成一些什么样的html呢.直接看效果吧. <div>          1.使用Ht

“XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不允许的问题

“XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不允许的问题 原因:设计页面继承的路径和后台.cs页面类的路径不一致造成的 看下图 这个是设计页面的样式 这个是后台cs文件代码 如果这两地方的路径不一致则会导致上述错误出现 解决方法:将这两个地方的路径统一即可解决

(转)System.Web.Mvc.UrlHelper的学习与使用

转载自http://www.cnblogs.com/longgel/archive/2010/02/06/1664884.html 上一次学习了HtmlHelper帮助类,这次我们学习一下UrlHelper帮助类,看类名也都知道这个类是用来帮我们生成URL在ASP.NET MVC应用程序中.让我们来看看该类给我们带来了哪些方便的方法和属性,UrlHelper提供了四个非常常用的四个方法, 1.Action方法通过提供Controller,Action和各种参数生成一个URL, 2.Content