zoukankan      html  css  js  c++  java
  • 浅从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 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 }
    View Code
  • 相关阅读:
    ASP.NET Interview Questions
    [上海]招聘.Net高级软件工程师&BI数据仓库工程师(急)
    访问被拒绝:“microsoft.web.ui.webcontrols”的解决办法
    上海市区广场、商场、大厦中英文对照大全
    文件夹加密码方法
    JavaScript:使用面向对象的技术创建高级 Web 应用程序(转)
    对数据类型而言运算符无效。运算符为 add,类型为 text。
    SQL行列转换
    主成分分析(Principal components analysis)最小平方误差解释
    主成分分析(Principal components analysis)最大方差解释
  • 原文地址:https://www.cnblogs.com/tdws/p/7087831.html
Copyright © 2011-2022 走看看