當前位置:
首頁 > 知識 > 淺從System.Web.Http.Owin的HttpMessageHandlerAdapter看適配器模式

淺從System.Web.Http.Owin的HttpMessageHandlerAdapter看適配器模式

本文版權歸博客園和作者吳雙本人共同所有 轉載和爬蟲請註明原文地址 www.cnblogs.com/tdws


一.寫在前面

適配器模式(Adapter)

可用來在現有介面和不兼容的類之間進行適配。有助於避免大規模改寫現有客戶代碼,其工作機制是對現有類的介面進行包裝,這樣客戶程序就能使用這個並非為其量身打造的類而又無需為此大動手術。 ----《JS設計模式》

將一個類的介面,轉換成客戶期望的另一個介面。適配器讓原本介面不兼容的類可以合作無間。

----《Head First設計模式》

這兩本書中對適配器模式定義如此,適配器模式在多種設計模式當中屬於比較容易理解的一種,其目的或者說可以解決的問題是新功能/新類型,不受原有類型/方法/功能的兼容,有了適配器這種巧妙地經驗,我們可以保證對修改封閉,對拓展開放。而達到此目的,正需要面向介面,並保持職責的單一性。也許對C#開發者來說,見的最多的就是SqlDataAdapter。


二.認識UseWebApi

本文所涉及OWIN,.NetFramework,Webapi 開源源碼下載地址為:

熟悉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, 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;
16 app.Use;
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 SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

DelegatingHandler實現了HttpMessageHandler,其構造函數中傳入HttpMessageHandler,並由同類對象innerHandler構成委託鏈。

1 protected DelegatingHandler(HttpMessageHandler innerHandler)
2 {
3 InnerHandler = innerHandler;
4 }
5
6 protected internal override Task 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 ///

25 /// Represents an OWIN component that submits requests to an when invoked.
26 ///

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 ///

Initializes a new instance of the class.

39 /// The next component in the pipeline. 40 /// The options to configure this adapter. 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 ///

Initializes a new instance of the class.

91 /// The next component in the pipeline. 92 /// The to submit requests to. 93 /// 94 /// The that determines whether or not to buffer requests and
95 /// responses.
96 /// 97 ///
98 /// This constructor is retained for backwards compatibility. The constructor taking
99 /// should be used instead.
100 ///

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 ///

Gets the to submit requests to.

109 public HttpMessageHandler MessageHandler
110 {
111 get { return _messageHandler; }
112 }
113
114 ///

115 /// Gets the that determines whether or not to buffer requests and
116 /// responses.
117 ///

118 public IHostBufferPolicySelector BufferPolicySelector
119 {
120 get { return _bufferPolicySelector; }
121 }
122
123 ///

Gets the to use to log unhandled exceptions.

124 public IExceptionLogger ExceptionLogger
125 {
126 get { return _exceptionLogger; }
127 }
128
129 ///

Gets the to use to process unhandled exceptions.

130 public IExceptionHandler ExceptionHandler
131 {
132 get { return _exceptionHandler; }
133 }
134
135 ///

Gets the that triggers cleanup of this component.

136 public CancellationToken AppDisposing
137 {
138 get { return _appDisposing; }
139 }
140
141 ///
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 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 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(HttpPropertyKeys.NoRouteMatched, out routingFailure)
349 && routingFailure)
350 {
351 return true;
352 }
353 }
354 return false;
355 }
356
357 private async Task 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 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 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 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 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 responseHeaders = owinResponse.Headers;
536 foreach (KeyValuePair> 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> 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 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 ///

633 /// Releases unmanaged and optionally managed resources.
634 ///

635 /// 636 /// to release both managed and unmanaged resources; to release
637 /// only unmanaged resources.
638 /// 639 ///
640 /// This class implements for legacy reasons. New callers should instead provide a
641 /// cancellation token via using the constructor that takes
642 /// .
643 ///

644 protected virtual void Dispose(bool disposing)
645 {
646 if (disposing)
647 {
648 OnAppDisposing;
649 }
650 }
651
652 ///
653 ///
654 /// This class implements for legacy reasons. New callers should instead provide a
655 /// cancellation token via using the constructor that takes
656 /// .
657 ///

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 }

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

gdb常用命令及使用gdb調試多進程多線程程序
WindowManager.LayoutParams的探究
運維腳本:索引統計
Spring Data JPA與PostgreSQL的jsonb類型集成與支持

TAG:達人科技 |

您可能感興趣

DisplayLink為Oculus Rift研發60GHz無線適配器參考設計
Valve砍掉Index頭顯VirtualLink適配器
外國公司Innerexile開啟ThunderMag磁吸適配器眾籌
Inclusive Technology發布通過滑鼠操縱iOS設備的適配器
V社取消Index VirtualLink適配器
Valve Index支持VirtualLink,但要額外購買適配器
CES 2019:DisplayLink展示DisplayLink XR無線適配器參考設計
Android 泛型與ArrayAdapter適配器 初步入門
AbleGamers和Twitch慶祝殘疾玩家 PS適配器確認用於PC
如何創建Istio Mixer適配器——來自Circonus公司的分享
Valve:Knuckles完全支持Vive無線適配器
如何用適配器使VirtualLink介面顯卡支持Oculus Rift頭
Surface筆記本Type-C適配器來了!
如何用適配器使VirtualLink介面顯卡支持Oculus Rift頭顯
如何為Oculus Rift設置TPCast無線適配器指南
Valve官方:Knuckles完全支持Vive無線適配器
Valve終放棄VR頭顯VirtualLink適配器
Cinch公司推出Johnson系列1.85 mm適配器和連接器
三星Galaxy Note 9支持Gear VR用戶可免費得適配器
【蘋果】iPhone8/8Plus標配的音頻適配器不再提供 | 下周預購的iPhone XR將挽頹勢