本文版權歸部落格園和作者吳雙本人共同所有 轉載和爬蟲請註明原文地址 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;
}
}
}
}

如果,您認為閱讀這篇部落格讓您有些收穫,不妨點選一下右下加【推薦】按鈕。
如果,您希望更容易地發現我的新部落格,不妨點選下方紅色【關注】的。
因為,我的分享熱情也離不開您的肯定支援。

感謝您的閱讀,我將持續輸出分享,我是蝸牛, 保持學習,謹記謙虛。不端不裝,有趣有夢。