Copyright of this article belongs to Blog Garden and author Wu Shuang himself. Please indicate the original address at www.cnblogs.com/tdws.
I. Write in front
Adapter mode
It can be used to adapt between existing interfaces and incompatible classes. It helps to avoid large-scale rewriting of existing client code by wrapping the interfaces of existing classes so that clients can use the classes that are not tailored for them without major surgery. JS Design Patterns
Convert the interface of a class into another interface that the customer expects. The adapter allows classes with incompatible interfaces to cooperate.
Head First Design Patterns
This is the definition of adapter pattern in these two books. The adapter pattern is one of the design patterns that is easy to understand. The purpose or problem that can be solved is new function/new type, which is not compatible with the original type/method/function. With the ingenious experience of adapter, we can ensure that the modification is closed and open to expansion. To achieve this goal, we need to face the interface and maintain the single character of responsibility. Perhaps for C# developers, SqlDataAdapter is the most common.
2. Understanding UseWebApi
This article deals with OWIN,.Net Framework, Webapi open source download address is:
https://github.com/aspnet/AspNetKatana
https://github.com/ASP-NET-MVC/aspnetwebstack
https://github.com/dotnet/corefx
Small partners familiar with OWIN system must have seen and used app.UseWebApi in Startip.cs. App is the object of IAppBuilder
Startup.cs is the startup class implemented by OWIN katana. As the name implies, Use WebApi is placed in the whole process as an OWIN middleware. app is the object of IAppBuilder, which is created by IAppBuilderFactory. IAppBuilder defines three methods, Build,New and Use. What are the three methods responsible for?
Build, which returns an instance of the OWIN pipe entry point, is called by the Init method in Microsoft. OWIN.Host. System Web. Its return instance will be converted to AppFun type. What is AppFun (using AppFunc = Func < IDictionary < string, object >, Task>)? It's an application delegation that the OWIN server interacts with the application. We can probably guess why this method is called in OWIN.Host.
New, used to return an AppBuilder instance, called and returned by IAppBuilderFactory.
Use is the method we often use in OWIN system. We can define our own OWIN middleware, according to its definition specification, and use it in processing pipelines, such as user operation log middleware, user identity verification middleware, etc.
At this point, we should clearly understand that WebApi is a middleware of OWIN. Take a chestnut:
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 // Introduce OWin.Cors Solving cross-domain access problems 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 }
Implementation of UseWebApi
Seeing this, you must ask why the UseWebapi method is not defined in IAppBuilder. UseWebapi is implemented in System.Web.Http.Owin's WebApiAppBuilder Extensions. cs. UseWebApi is a C this extension method, and the answer you think is no different. In its implementation, builder. Use (typeof (HttpMessageHandler Adapter, options) is called;
At this point, do not blame me me for a few words, the implementation steps of Adapter: in order to make a class or a function compatible with existing classes/interfaces, then
1. The adapter implements the interface or abstract method of the target customer for parameter input
2. Calling the method of the target customer in the method of the implemented interface/abstract class
The protagonist HttpMessageHandler Adapter finally appears, and the little friends who have learned about the Adapter pattern can certainly imagine that since HttpMessageHandler is Adadapter, there must be a private field defined in the class, and the type is HttpMessageHandler. You can also imagine that the adapter inherits the abstract type of Owing Middleware and implements its In-In. The voke Abstract method, in a method of HttpMessageHandler Adapter, must call the method of HttpMessageHandler. So from the source code, we know that the field of HttpMessageHandler is named _messageHandler. (Is it similar to the above-mentioned Adapter implementation, which may not be well summarized? I suggest referring to more articles and examples.)
Asp.Net Webapi's message processing pipeline is a processing pipeline composed of HttpMessageHandler's delegation chain
The top one and only abstract method in the abstract class of HttpMessageHandler is used for implementation. Its input is HttpRequestMessage and its output is HttpResponseMessage.
1 protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
Delegating Handler implements HttpMessageHandler, whose constructor passes in HttpMessageHandler, and a delegation chain is formed by innerHandler of the same kind.
Recommend an MS document h ttps://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers If you are interested, you can refer to it a little.
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 }
In the middle, we have a long list to illustrate the role of HttpMessageHandler, so that we can further understand why HttpMessageHandler Adapter exists and pass this type in when using (WebApi middleware).
In the HttpMessageHandler Adapter constructor, _messageHandler is wrapped as the HttpMessageInvoker type, which is intended to provide a specialized class for invoking the SendAsync method.
We have just learned that HttpMessageHandler Adapter implements OWinMiddleware, so we can understand from the source code what we have done in the abstract method Invoke: it calls the InvokeCore method of the same kind, and creates HttpRequestMessage in InvokeCore, and takes its object as SendAsync's reference, and finally gets the HttpResponseMessage object.
IV. Write at the end
In this way, once through the source code reading, once again on the understanding of the Adapter, HttpMessageHandler Adapter finally through the implementation of Owin Middleware, in Invoke through the HttpMessageInvoker call SendAsync, dropped into HttpRequestMessage, get ResponseMessage. Finally attached with the HttpMessageHandler Adapter source code, more source code, or from the connection in the second paragraph down. Take it.
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 }
If you think reading this blog will give you some benefit, click on the bottom right to add [Recommendation] Button.
If you want to find my new blog more easily, click on the red one below.
Because my enthusiasm for sharing can not be separated from your affirmative support.
Thank you for your reading. I will continue to export and share. I am a snail. Keep learning and remember to be modest. It's funny and dreamy.