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

时间:2022-01-19 23:13:32

本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 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的一个中间件而已了吧。举个栗子:

 public partial class Startup
{ public void Configuration(IAppBuilder app)
{
// This must happen FIRST otherwise CORS will not work.
// 引入OWin.Cors 解决跨域访问问题
app.UseCors(CorsOptions.AllowAll); GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); ConfigureAuth(app); app.Use<UserContextMiddleware>();
app.Use<UserOperationMiddleware>();
app.UseWebApi(GlobalConfiguration.Configuration);
}
}

三.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。

 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,有兴趣可以稍微参照下。

         protected DelegatingHandler(HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler;
} protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest);
}
SetOperationStarted();
return _innerHandler.SendAsync(request, cancellationToken);
}

中间啰嗦了一串,为了说明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源码,更多源码,还是从第二段的连接中下载吧。

 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

 using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.ExceptionServices;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Hosting;
using System.Web.Http.Owin.ExceptionHandling;
using System.Web.Http.Owin.Properties;
using Microsoft.Owin; namespace System.Web.Http.Owin
{
/// <summary>
/// Represents an OWIN component that submits requests to an <see cref="HttpMessageHandler"/> when invoked.
/// </summary>
public class HttpMessageHandlerAdapter : OwinMiddleware, IDisposable
{
private readonly HttpMessageHandler _messageHandler;
private readonly HttpMessageInvoker _messageInvoker;
private readonly IHostBufferPolicySelector _bufferPolicySelector;
private readonly IExceptionLogger _exceptionLogger;
private readonly IExceptionHandler _exceptionHandler;
private readonly CancellationToken _appDisposing; private bool _disposed; /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
/// <param name="next">The next component in the pipeline.</param>
/// <param name="options">The options to configure this adapter.</param>
public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandlerOptions options)
: base(next)
{
if (options == null)
{
throw new ArgumentNullException("options");
} _messageHandler = options.MessageHandler; if (_messageHandler == null)
{
throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
typeof(HttpMessageHandlerOptions).Name, "MessageHandler"), "options");
} _messageInvoker = new HttpMessageInvoker(_messageHandler);
_bufferPolicySelector = options.BufferPolicySelector; if (_bufferPolicySelector == null)
{
throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
typeof(HttpMessageHandlerOptions).Name, "BufferPolicySelector"), "options");
} _exceptionLogger = options.ExceptionLogger; if (_exceptionLogger == null)
{
throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
typeof(HttpMessageHandlerOptions).Name, "ExceptionLogger"), "options");
} _exceptionHandler = options.ExceptionHandler; if (_exceptionHandler == null)
{
throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull,
typeof(HttpMessageHandlerOptions).Name, "ExceptionHandler"), "options");
} _appDisposing = options.AppDisposing; if (_appDisposing.CanBeCanceled)
{
_appDisposing.Register(OnAppDisposing);
}
} /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary>
/// <param name="next">The next component in the pipeline.</param>
/// <param name="messageHandler">The <see cref="HttpMessageHandler"/> to submit requests to.</param>
/// <param name="bufferPolicySelector">
/// The <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
/// responses.
/// </param>
/// <remarks>
/// This constructor is retained for backwards compatibility. The constructor taking
/// <see cref="HttpMessageHandlerOptions"/> should be used instead.
/// </remarks>
[Obsolete("Use the HttpMessageHandlerAdapter(OwinMiddleware, HttpMessageHandlerOptions) constructor instead.")]
public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandler messageHandler,
IHostBufferPolicySelector bufferPolicySelector)
: this(next, CreateOptions(messageHandler, bufferPolicySelector))
{
} /// <summary>Gets the <see cref="HttpMessageHandler"/> to submit requests to.</summary>
public HttpMessageHandler MessageHandler
{
get { return _messageHandler; }
} /// <summary>
/// Gets the <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and
/// responses.
/// </summary>
public IHostBufferPolicySelector BufferPolicySelector
{
get { return _bufferPolicySelector; }
} /// <summary>Gets the <see cref="IExceptionLogger"/> to use to log unhandled exceptions.</summary>
public IExceptionLogger ExceptionLogger
{
get { return _exceptionLogger; }
} /// <summary>Gets the <see cref="IExceptionHandler"/> to use to process unhandled exceptions.</summary>
public IExceptionHandler ExceptionHandler
{
get { return _exceptionHandler; }
} /// <summary>Gets the <see cref="CancellationToken"/> that triggers cleanup of this component.</summary>
public CancellationToken AppDisposing
{
get { return _appDisposing; }
} /// <inheritdoc />
public override Task Invoke(IOwinContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
} IOwinRequest owinRequest = context.Request;
IOwinResponse owinResponse = context.Response; if (owinRequest == null)
{
throw Error.InvalidOperation(OwinResources.OwinContext_NullRequest);
}
if (owinResponse == null)
{
throw Error.InvalidOperation(OwinResources.OwinContext_NullResponse);
} return InvokeCore(context, owinRequest, owinResponse);
} private async Task InvokeCore(IOwinContext context, IOwinRequest owinRequest, IOwinResponse owinResponse)
{
CancellationToken cancellationToken = owinRequest.CallCancelled;
HttpContent requestContent; bool bufferInput = _bufferPolicySelector.UseBufferedInputStream(hostContext: context); if (!bufferInput)
{
owinRequest.DisableBuffering();
} if (!owinRequest.Body.CanSeek && bufferInput)
{
requestContent = await CreateBufferedRequestContentAsync(owinRequest, cancellationToken);
}
else
{
requestContent = CreateStreamedRequestContent(owinRequest);
} HttpRequestMessage request = CreateRequestMessage(owinRequest, requestContent);
MapRequestProperties(request, context); SetPrincipal(owinRequest.User); HttpResponseMessage response = null;
bool callNext; try
{
response = await _messageInvoker.SendAsync(request, cancellationToken); // Handle null responses
if (response == null)
{
throw Error.InvalidOperation(OwinResources.SendAsync_ReturnedNull);
} // Handle soft 404s where no route matched - call the next component
if (IsSoftNotFound(request, response))
{
callNext = true;
}
else
{
callNext = false; // Compute Content-Length before calling UseBufferedOutputStream because the default implementation
// accesses that header and we want to catch any exceptions calling TryComputeLength here. if (response.Content == null
|| await ComputeContentLengthAsync(request, response, owinResponse, cancellationToken))
{
bool bufferOutput = _bufferPolicySelector.UseBufferedOutputStream(response); if (!bufferOutput)
{
owinResponse.DisableBuffering();
}
else if (response.Content != null)
{
response = await BufferResponseContentAsync(request, response, cancellationToken);
} if (await PrepareHeadersAsync(request, response, owinResponse, cancellationToken))
{
await SendResponseMessageAsync(request, response, owinResponse, cancellationToken);
}
}
}
}
finally
{
request.DisposeRequestResources();
request.Dispose();
if (response != null)
{
response.Dispose();
}
} // Call the next component if no route matched
if (callNext && Next != null)
{
await Next.Invoke(context);
}
} private static HttpContent CreateStreamedRequestContent(IOwinRequest owinRequest)
{
// Note that we must NOT dispose owinRequest.Body in this case. Disposing it would close the input
// stream and prevent cascaded components from accessing it. The server MUST handle any necessary
// cleanup upon request completion. NonOwnedStream prevents StreamContent (or its callers including
// HttpRequestMessage) from calling Close or Dispose on owinRequest.Body.
return new StreamContent(new NonOwnedStream(owinRequest.Body));
} private static async Task<HttpContent> CreateBufferedRequestContentAsync(IOwinRequest owinRequest,
CancellationToken cancellationToken)
{
// We need to replace the request body with a buffered stream so that other components can read the stream.
// For this stream to be useful, it must NOT be diposed along with the request. Streams created by
// StreamContent do get disposed along with the request, so use MemoryStream to buffer separately.
MemoryStream buffer;
int? contentLength = owinRequest.GetContentLength(); if (!contentLength.HasValue)
{
buffer = new MemoryStream();
}
else
{
buffer = new MemoryStream(contentLength.Value);
} cancellationToken.ThrowIfCancellationRequested(); using (StreamContent copier = new StreamContent(owinRequest.Body))
{
await copier.CopyToAsync(buffer);
} // Provide the non-disposing, buffered stream to later OWIN components (set to the stream's beginning).
buffer.Position = ;
owinRequest.Body = buffer; // For MemoryStream, Length is guaranteed to be an int.
return new ByteArrayContent(buffer.GetBuffer(), , (int)buffer.Length);
} private static HttpRequestMessage CreateRequestMessage(IOwinRequest owinRequest, HttpContent requestContent)
{
// Create the request
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod(owinRequest.Method), owinRequest.Uri); try
{
// Set the body
request.Content = requestContent; // Copy the headers
foreach (KeyValuePair<string, string[]> header in owinRequest.Headers)
{
if (!request.Headers.TryAddWithoutValidation(header.Key, header.Value))
{
bool success = requestContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
Contract.Assert(success,
"Every header can be added either to the request headers or to the content headers");
}
}
}
catch
{
request.Dispose();
throw;
} return request;
} private static void MapRequestProperties(HttpRequestMessage request, IOwinContext context)
{
// Set the OWIN context on the request
request.SetOwinContext(context); // Set a request context on the request that lazily populates each property.
HttpRequestContext requestContext = new OwinHttpRequestContext(context, request);
request.SetRequestContext(requestContext);
} private static void SetPrincipal(IPrincipal user)
{
if (user != null)
{
Thread.CurrentPrincipal = user;
}
} private static bool IsSoftNotFound(HttpRequestMessage request, HttpResponseMessage response)
{
if (response.StatusCode == HttpStatusCode.NotFound)
{
bool routingFailure;
if (request.Properties.TryGetValue<bool>(HttpPropertyKeys.NoRouteMatched, out routingFailure)
&& routingFailure)
{
return true;
}
}
return false;
} private async Task<HttpResponseMessage> BufferResponseContentAsync(HttpRequestMessage request,
HttpResponseMessage response, CancellationToken cancellationToken)
{
ExceptionDispatchInfo exceptionInfo; cancellationToken.ThrowIfCancellationRequested(); try
{
await response.Content.LoadIntoBufferAsync();
return response;
}
catch (OperationCanceledException)
{
// Propogate the canceled task without calling exception loggers or handlers.
throw;
}
catch (Exception exception)
{
exceptionInfo = ExceptionDispatchInfo.Capture(exception);
} // If the content can't be buffered, create a buffered error response for the exception
// This code will commonly run when a formatter throws during the process of serialization Debug.Assert(exceptionInfo.SourceException != null); ExceptionContext exceptionContext = new ExceptionContext(exceptionInfo.SourceException,
OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferContent, request, response); await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
HttpResponseMessage errorResponse = await _exceptionHandler.HandleAsync(exceptionContext,
cancellationToken); response.Dispose(); if (errorResponse == null)
{
exceptionInfo.Throw();
return null;
} // We have an error response to try to buffer and send back. response = errorResponse;
cancellationToken.ThrowIfCancellationRequested(); Exception errorException; try
{
// Try to buffer the error response and send it back.
await response.Content.LoadIntoBufferAsync();
return response;
}
catch (OperationCanceledException)
{
// Propogate the canceled task without calling exception loggers.
throw;
}
catch (Exception exception)
{
errorException = exception;
} // We tried to send back an error response with content, but we couldn't. It's an edge case; the best we
// can do is to log that exception and send back an empty 500. ExceptionContext errorExceptionContext = new ExceptionContext(errorException,
OwinExceptionCatchBlocks.HttpMessageHandlerAdapterBufferError, request, response);
await _exceptionLogger.LogAsync(errorExceptionContext, cancellationToken); response.Dispose();
return request.CreateResponse(HttpStatusCode.InternalServerError);
} // Prepares Content-Length and Transfer-Encoding headers.
private Task<bool> PrepareHeadersAsync(HttpRequestMessage request, HttpResponseMessage response,
IOwinResponse owinResponse, CancellationToken cancellationToken)
{
Contract.Assert(response != null);
HttpResponseHeaders responseHeaders = response.Headers;
Contract.Assert(responseHeaders != null);
HttpContent content = response.Content;
bool isTransferEncodingChunked = responseHeaders.TransferEncodingChunked == true;
HttpHeaderValueCollection<TransferCodingHeaderValue> transferEncoding = responseHeaders.TransferEncoding; if (content != null)
{
HttpContentHeaders contentHeaders = content.Headers;
Contract.Assert(contentHeaders != null); if (isTransferEncodingChunked)
{
// According to section 4.4 of the HTTP 1.1 spec, HTTP responses that use chunked transfer
// encoding must not have a content length set. Chunked should take precedence over content
// length in this case because chunked is always set explicitly by users while the Content-Length
// header can be added implicitly by System.Net.Http.
contentHeaders.ContentLength = null;
}
else
{
// Copy the response content headers only after ensuring they are complete.
// We ask for Content-Length first because HttpContent lazily computes this header and only
// afterwards writes the value into the content headers.
return ComputeContentLengthAsync(request, response, owinResponse, cancellationToken);
}
} // Ignore the Transfer-Encoding header if it is just "chunked"; the host will likely provide it when no
// Content-Length is present (and if the host does not, there's not much better this code could do to
// transmit the current response, since HttpContent is assumed to be unframed; in that case, silently drop
// the Transfer-Encoding: chunked header).
// HttpClient sets this header when it receives chunked content, but HttpContent does not include the
// frames. The OWIN contract is to set this header only when writing chunked frames to the stream.
// A Web API caller who desires custom framing would need to do a different Transfer-Encoding (such as
// "identity, chunked").
if (isTransferEncodingChunked && transferEncoding.Count == )
{
transferEncoding.Clear();
} return Task.FromResult(true);
} [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "unused",
Justification = "unused variable necessary to call getter")]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Exception is turned into an error response.")]
private Task<bool> ComputeContentLengthAsync(HttpRequestMessage request, HttpResponseMessage response,
IOwinResponse owinResponse, CancellationToken cancellationToken)
{
Contract.Assert(response != null);
HttpResponseHeaders responseHeaders = response.Headers;
Contract.Assert(responseHeaders != null);
HttpContent content = response.Content;
Contract.Assert(content != null);
HttpContentHeaders contentHeaders = content.Headers;
Contract.Assert(contentHeaders != null); Exception exception; try
{
var unused = contentHeaders.ContentLength; return Task.FromResult(true);
}
catch (Exception ex)
{
exception = ex;
} return HandleTryComputeLengthExceptionAsync(exception, request, response, owinResponse, cancellationToken);
} private async Task<bool> HandleTryComputeLengthExceptionAsync(Exception exception, HttpRequestMessage request,
HttpResponseMessage response, IOwinResponse owinResponse, CancellationToken cancellationToken)
{
Contract.Assert(owinResponse != null); ExceptionContext exceptionContext = new ExceptionContext(exception,
OwinExceptionCatchBlocks.HttpMessageHandlerAdapterComputeContentLength, request, response);
await _exceptionLogger.LogAsync(exceptionContext, cancellationToken); // Send back an empty error response if TryComputeLength throws.
owinResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
SetHeadersForEmptyResponse(owinResponse.Headers);
return false;
} private Task SendResponseMessageAsync(HttpRequestMessage request, HttpResponseMessage response,
IOwinResponse owinResponse, CancellationToken cancellationToken)
{
owinResponse.StatusCode = (int)response.StatusCode;
owinResponse.ReasonPhrase = response.ReasonPhrase; // Copy non-content headers
IDictionary<string, string[]> responseHeaders = owinResponse.Headers;
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
{
responseHeaders[header.Key] = header.Value.AsArray();
} HttpContent responseContent = response.Content;
if (responseContent == null)
{
SetHeadersForEmptyResponse(responseHeaders);
return TaskHelpers.Completed();
}
else
{
// Copy content headers
foreach (KeyValuePair<string, IEnumerable<string>> contentHeader in responseContent.Headers)
{
responseHeaders[contentHeader.Key] = contentHeader.Value.AsArray();
} // Copy body
return SendResponseContentAsync(request, response, owinResponse, cancellationToken);
}
} private static void SetHeadersForEmptyResponse(IDictionary<string, string[]> headers)
{
// Set the content-length to 0 to prevent the server from sending back the response chunked
headers["Content-Length"] = new string[] { "" };
} private async Task SendResponseContentAsync(HttpRequestMessage request, HttpResponseMessage response,
IOwinResponse owinResponse, CancellationToken cancellationToken)
{
Contract.Assert(response != null);
Contract.Assert(response.Content != null); Exception exception;
cancellationToken.ThrowIfCancellationRequested(); try
{
await response.Content.CopyToAsync(owinResponse.Body);
return;
}
catch (OperationCanceledException)
{
// Propogate the canceled task without calling exception loggers;
throw;
}
catch (Exception ex)
{
exception = ex;
} // We're streaming content, so we can only call loggers, not handlers, as we've already (possibly) send the
// status code and headers across the wire. Log the exception, but then just abort.
ExceptionContext exceptionContext = new ExceptionContext(exception,
OwinExceptionCatchBlocks.HttpMessageHandlerAdapterStreamContent, request, response);
await _exceptionLogger.LogAsync(exceptionContext, cancellationToken);
await AbortResponseAsync();
} private static Task AbortResponseAsync()
{
// OWIN doesn't yet support an explicit Abort event. Returning a canceled task is the best contract at the
// moment.
return TaskHelpers.Canceled();
} // Provides HttpMessageHandlerOptions for callers using the old constructor.
private static HttpMessageHandlerOptions CreateOptions(HttpMessageHandler messageHandler,
IHostBufferPolicySelector bufferPolicySelector)
{
if (messageHandler == null)
{
throw new ArgumentNullException("messageHandler");
} if (bufferPolicySelector == null)
{
throw new ArgumentNullException("bufferPolicySelector");
} // Callers using the old constructor get the default exception handler, no exception logging support, and no
// app cleanup support. return new HttpMessageHandlerOptions
{
MessageHandler = messageHandler,
BufferPolicySelector = bufferPolicySelector,
ExceptionLogger = new EmptyExceptionLogger(),
ExceptionHandler = new DefaultExceptionHandler(),
AppDisposing = CancellationToken.None
};
} /// <summary>
/// Releases unmanaged and optionally managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release
/// only unmanaged resources.
/// </param>
/// <remarks>
/// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
/// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
/// <see cref="HttpMessageHandlerOptions"/>.
/// </remarks>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
OnAppDisposing();
}
} /// <inheritdoc />
/// <remarks>
/// This class implements <see cref="IDisposable"/> for legacy reasons. New callers should instead provide a
/// cancellation token via <see cref="AppDisposing"/> using the constructor that takes
/// <see cref="HttpMessageHandlerOptions"/>.
/// </remarks>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} private void OnAppDisposing()
{
if (!_disposed)
{
_messageInvoker.Dispose();
_disposed = true;
}
}
}
}

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下加【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击下方红色【关注】的。
因为,我的分享热情也离不开您的肯定支持。

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