从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式
Posted 蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式相关的知识,希望对你有一定的参考价值。
本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws
一.写在前面
适配器模式(Adapter)
可用来在现有接口和不兼容的类之间进行适配。有助于避免大规模改写现有客户代码,其工作机制是对现有类的接口进行包装,这样客户程序就能使用这个并非为其量身打造的类而又无需为此大动手术。 ----《JS设计模式》
将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
----《Head First设计模式》
这两本书中对适配器模式定义如此,适配器模式在多种设计模式当中属于比较容易理解的一种,其目的或者说可以解决的问题是新功能/新类型,不受原有类型/方法/功能的兼容,有了适配器这种巧妙地经验,我们可以保证对修改封闭,对拓展开放。而达到此目的,正需要面向接口,并保持职责的单一性。也许对C#开发者来说,见的最多的就是SqlDataAdapter。
二.认识UseWebApi
本文所涉及OWIN,.NetFramework,Webapi 开源源码下载地址为:
https://github.com/aspnet/AspNetKatana
https://github.com/ASP-NET-MVC/aspnetwebstack
https://github.com/dotnet/corefx
熟悉OWIN体系的小伙伴们,一定都在Startup.cs中见过也使用过app.UseWebApi吧。app是IAppBuilder的对象
Startup.cs是OWIN katana实现的启动类,刚说的UseWebApi顾名思义,就是将WebApi作为一个OWIN中间件放在整个处理流程中。app是IAppBuilder的对象,其创建由IAppBuilderFactory负责。IAppBuilder定义了三个方法,分别为Build,New和Use. 这三个方法分别负责什么呢?
Build,返回OWIN管道入口点的一个实例,由 Microsoft.Owin.Host.SystemWeb中的Init方法调用。其返回实例将被转换为AppFun类型,AppFun( using AppFunc = Func<IDictionary<string, object>, Task>;)是什么呢?它是OWIN服务器与应用程序交互的应用程序委托,我们看到这个方法在OWIN.Host中调用,应该就能大概猜到个所以然。
New,用于返回一个AppBuilder实例,由IAppBuilderFactory调用并返回。
Use,就是我们在OWIN体系中,经常使用到的方法,我们可以定义自己的OWIN中间件,按照其定义规范,并Use到处理管道中,比如用户操作日志中间件,用户身份校验中间件等。
说到这里,我们应该很清晰的了解到WebApi是OWIN的一个中间件而已了吧。举个栗子:
1 public partial class Startup 2 { 3 4 public void Configuration(IAppBuilder app) 5 { 6 // This must happen FIRST otherwise CORS will not work. 7 // 引入OWin.Cors 解决跨域访问问题 8 app.UseCors(CorsOptions.AllowAll); 9 10 GlobalConfiguration.Configure(WebApiConfig.Register); 11 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 12 13 ConfigureAuth(app); 14 15 app.Use<UserContextMiddleware>(); 16 app.Use<UserOperationMiddleware>(); 17 app.UseWebApi(GlobalConfiguration.Configuration); 18 } 19 }
三.UseWebApi的实现
看到这里你一定会问,为什么IAppBuilder中没有定义UseWebapi方法呢,UseWebapi的实现在System.Web.Http.Owin的WebApiAppBuilderExtensions.cs中,UseWebApi是一个C# this拓展方法,和你所想到的答案并无差。在其实现中,调用了 builder.Use(typeof(HttpMessageHandlerAdapter), options);
到这里,一定要啰嗦几句不要怪我,Adapter的实现步骤:为了使一个类或者一个功能,兼容已有类/接口,那么
1.被适配器实现目标客户的接口或抽象方法,以便参数的传入
2.所实现接口/抽象类的方法中调用目标客户的方法
HttpMessageHandlerAdapter 这个主角终于出现了,对Adapter模式了解后的小伙伴们也一定能想得到,既然是HttpMessageHandler的Adapter,那么 在其类中 一定定义了一个private的字段,并且类型为HttpMessageHandler,你也一定能想得到这个Adapter继承了OwinMiddleware这个抽象类型并且实现其Invoke抽象方法,在HttpMessageHandlerAdapter的一个方法中一定调用了HttpMessageHandler的方法。那么通过源码我们了解到HttpMessageHandler的字段名为_messageHandler。(是不是和上面所说的Adapter实现方式类似呢,实现方式可能概括的不好,建议参阅更多文章和范例)
Asp.Net Webapi的消息处理管道是由HttpMessageHandler的委托链所组成的处理管道
HttpMessageHandler抽象类当中顶一个一个唯一的抽象方法用于实现,其入参为HttpRequestMessage,其出参为HttpResponseMessage。
1 protected internal abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
DelegatingHandler实现了HttpMessageHandler,其构造函数中传入HttpMessageHandler,并由同类对象innerHandler构成委托链。
推荐一篇MS文档 https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/http-message-handlers,有兴趣可以稍微参照下。
1 protected DelegatingHandler(HttpMessageHandler innerHandler) 2 { 3 InnerHandler = innerHandler; 4 } 5 6 protected internal override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 7 { 8 if (request == null) 9 { 10 throw new ArgumentNullException(nameof(request), SR.net_http_handler_norequest); 11 } 12 SetOperationStarted(); 13 return _innerHandler.SendAsync(request, cancellationToken); 14 }
中间啰嗦了一串,为了说明HttpMessageHandler的作用,这样我们能进一步理解,为什么要有HttpMessageHandlerAdapter的存在,并在Use (WebApi中间件)的时候,将该类型传入。
在HttpMessageHandlerAdapter构造函数中,_messageHandler被包装为HttpMessageInvoker类型,这个类型的目的是提供一个专门的类,用于调用SendAsync方法。
刚才我们已经了解到HttpMessageHandlerAdapter实现了OWinMiddleware, 那么我们从源码中了解下,在其实现的抽象方法Invoke中,做了什么事情:其调用同类下的InvokeCore方法,InvokeCore中Create了HttpRequestMessage,并将其对象作为SendAsync的入参,最后得到HttpResponseMessage对象。
四.写在最后
就是这样,一次通过源码的阅读,再次对Adapter的理解,HttpMessageHandlerAdapter最终通过对OwinMiddleware的实现,在Invoke中通过HttpMessageInvoker调用执行SendAsync,丢入HttpRequestMessage,拿到ResponseMessage.最终附上HttpMessageHandlerAdapter源码,更多源码,还是从第二段的连接中下载吧。
1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. 2 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using System.Diagnostics.CodeAnalysis; 6 using System.Diagnostics.Contracts; 7 using System.IO; 8 using System.Net; 9 using System.Net.Http; 10 using System.Net.Http.Headers; 11 using System.Runtime.ExceptionServices; 12 using System.Security.Principal; 13 using System.Threading; 14 using System.Threading.Tasks; 15 using System.Web.Http.Controllers; 16 using System.Web.Http.ExceptionHandling; 17 using System.Web.Http.Hosting; 18 using System.Web.Http.Owin.ExceptionHandling; 19 using System.Web.Http.Owin.Properties; 20 using Microsoft.Owin; 21 22 namespace System.Web.Http.Owin 23 { 24 /// <summary> 25 /// Represents an OWIN component that submits requests to an <see cref="HttpMessageHandler"/> when invoked. 26 /// </summary> 27 public class HttpMessageHandlerAdapter : OwinMiddleware, IDisposable 28 { 29 private readonly HttpMessageHandler _messageHandler; 30 private readonly HttpMessageInvoker _messageInvoker; 31 private readonly IHostBufferPolicySelector _bufferPolicySelector; 32 private readonly IExceptionLogger _exceptionLogger; 33 private readonly IExceptionHandler _exceptionHandler; 34 private readonly CancellationToken _appDisposing; 35 36 private bool _disposed; 37 38 /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary> 39 /// <param name="next">The next component in the pipeline.</param> 40 /// <param name="options">The options to configure this adapter.</param> 41 public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandlerOptions options) 42 : base(next) 43 { 44 if (options == null) 45 { 46 throw new ArgumentNullException("options"); 47 } 48 49 _messageHandler = options.MessageHandler; 50 51 if (_messageHandler == null) 52 { 53 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull, 54 typeof(HttpMessageHandlerOptions).Name, "MessageHandler"), "options"); 55 } 56 57 _messageInvoker = new HttpMessageInvoker(_messageHandler); 58 _bufferPolicySelector = options.BufferPolicySelector; 59 60 if (_bufferPolicySelector == null) 61 { 62 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull, 63 typeof(HttpMessageHandlerOptions).Name, "BufferPolicySelector"), "options"); 64 } 65 66 _exceptionLogger = options.ExceptionLogger; 67 68 if (_exceptionLogger == null) 69 { 70 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull, 71 typeof(HttpMessageHandlerOptions).Name, "ExceptionLogger"), "options"); 72 } 73 74 _exceptionHandler = options.ExceptionHandler; 75 76 if (_exceptionHandler == null) 77 { 78 throw new ArgumentException(Error.Format(OwinResources.TypePropertyMustNotBeNull, 79 typeof(HttpMessageHandlerOptions).Name, "ExceptionHandler"), "options"); 80 } 81 82 _appDisposing = options.AppDisposing; 83 84 if (_appDisposing.CanBeCanceled) 85 { 86 _appDisposing.Register(OnAppDisposing); 87 } 88 } 89 90 /// <summary>Initializes a new instance of the <see cref="HttpMessageHandlerAdapter"/> class.</summary> 91 /// <param name="next">The next component in the pipeline.</param> 92 /// <param name="messageHandler">The <see cref="HttpMessageHandler"/> to submit requests to.</param> 93 /// <param name="bufferPolicySelector"> 94 /// The <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and 95 /// responses. 96 /// </param> 97 /// <remarks> 98 /// This constructor is retained for backwards compatibility. The constructor taking 99 /// <see cref="HttpMessageHandlerOptions"/> should be used instead. 100 /// </remarks> 101 [Obsolete("Use the HttpMessageHandlerAdapter(OwinMiddleware, HttpMessageHandlerOptions) constructor instead.")] 102 public HttpMessageHandlerAdapter(OwinMiddleware next, HttpMessageHandler messageHandler, 103 IHostBufferPolicySelector bufferPolicySelector) 104 : this(next, CreateOptions(messageHandler, bufferPolicySelector)) 105 { 106 } 107 108 /// <summary>Gets the <see cref="HttpMessageHandler"/> to submit requests to.</summary> 109 public HttpMessageHandler MessageHandler 110 { 111 get { return _messageHandler; } 112 } 113 114 /// <summary> 115 /// Gets the <see cref="IHostBufferPolicySelector"/> that determines whether or not to buffer requests and 116 /// responses. 117 /// </summary> 118 public IHostBufferPolicySelector BufferPolicySelector 119 { 120 get { return _bufferPolicySelector; } 121 } 122 123 /// <summary>Gets the <see cref="IExceptionLogger"/> to use to log unhandled exceptions.</summary> 124 public IExceptionLogger ExceptionLogger 125 { 126 get { return _exceptionLogger; } 127 } 128 129 /// <summary>Gets the <see cref="IExceptionHandler"/> to use to process unhandled exceptions.</summary> 130 public IExceptionHandler ExceptionHandler 131 { 132 get { return _exceptionHandler; } 133 } 134 135 /// <summary>Gets the <see cref="CancellationToken"/> that triggers cleanup of this component.</summary> 136 public CancellationToken AppDisposing 137 { 138 get { return _appDisposing; } 139 } 140 141 /// <inheritdoc /> 142 public override Task Invoke(IOwinContext context) 143 { 144 if (context == null) 145 { 146 throw new ArgumentNullException("context"); 147 } 148 149 IOwinRequest owinRequest = context.Request; 150 IOwinResponse owinResponse = context.Response; 151 152 if (owinRequest == null) 153 { 154 throw Error.InvalidOperation(OwinResources.OwinContext_NullRequest); 155 } 156 if (owinResponse == null) 157 { 158 throw Error.InvalidOperation(OwinResources.OwinContext_NullResponse); 159 } 160 161 return InvokeCore(context, owinRequest, owinResponse); 162 } 163 164 private async Task InvokeCore(IOwinContext context, IOwinRequest owinRequest, IOwinResponse owinResponse) 165 { 166 CancellationToken cancellationToken = owinRequest.CallCancelled; 167 HttpContent requestContent; 168 169 bool bufferInput = _bufferPolicySelector.UseBufferedInputStream(hostContext: context); 170 171 if (!bufferInput) 172 { 173 owinRequest.DisableBuffering(); 174 } 175 176 if (!owinRequest.Body.CanSeek && bufferInput) 177 { 178 requestContent = await CreateBufferedRequestContentAsync(owinRequest, cancellationToken); 179 } 180 else 181 { 182 requestContent = CreateStreamedRequestContent(owinRequest); 183 } 184 185 HttpRequestMessage request = CreateRequestMessage(owinRequest, requestContent); 186 MapRequestProperties(request, context); 187 188 SetPrincipal(owinRequest.User); 189 190 HttpResponseMessage response = null; 191 bool callNext; 192 193 try 194 { 195 response = await _messageInvoker.SendAsync(request, cancellationToken); 196 197 // Handle null responses 198 if (response == null) 199 { 200 throw Error.InvalidOperation(OwinResources.SendAsync_ReturnedNull); 201 } 202 203 // Handle soft 404s where no route matched - call the next component 204 if (IsSoftNotFound(request, response)) 205 { 206 callNext = true; 207 } 208 else 209 { 210 callNext = false; 211 212 // Compute Content-Length before calling UseBufferedOutputStream because the default implementation 213 // accesses that header and we want to catch any exceptions calling TryComputeLength here. 214 215 if (response.Content == null 216 || await ComputeContentLengthAsync(request, response, owinResponse, cancellationToken)) 217 { 218 bool bufferOutput = _bufferPolicySelector.UseBufferedOutputStream(response); 219 220 if (!bufferOutput) 221 { 222 owinResponse.DisableBuffering(); 223 } 224 else if (response.Content != null) 225 { 226 response = await BufferResponseContentAsync(request, response, cancellationToken); 227 } 228 229 if (await PrepareHeadersAsync(request, response, owinResponse, cancellationToken)) 230 { 231 await SendResponseMessageAsync(request, response, owinResponse, cancellationToken); 232 } 233 } 234 } 235 } 236 finally 237 { 238 request.DisposeRequestResources(); 239 request.Dispose(); 240 if (response != null) 241 { 242 response.Dispose(); 243 } 244 } 245 246 // Call the next component if no route matched 247 if (callNext && Next != null) 248 { 249 await Next.Invoke(context); 250 } 251 } 252 253 private static HttpContent CreateStreamedRequestContent(IOwinRequest owinRequest) 254 { 255 // Note that we must NOT dispose owinRequest.Body in this case. Disposing it would close the input 256 // stream and prevent cascaded components from accessing it. The server MUST handle any necessary 257 // cleanup upon request completion. NonOwnedStream prevents StreamContent (or its callers including 258 // HttpRequestMessage) from calling Close or Dispose on owinRequest.Body. 259 return new StreamContent(new NonOwnedStream(owinRequest.Body)); 260 } 261 262 private static async Task<HttpContent> CreateBufferedRequestContentAsync(IOwinRequest owinRequest, 263 CancellationToken cancellationToken) 264 { 265 // We need to replace the request body with a buffered stream so that other components can read the stream. 266 // For this stream to be useful, it must NOT be diposed along with the request. Streams created by 267 // StreamContent do get disposed along with the request, so use MemoryStream to buffer separately. 268 MemoryStream buffer; 269 int? contentLength = owinRequest.GetContentLength(); 270 271 if (!contentLength.HasValue) 272 { 273 buffer = new MemoryStream(); 274 } 275 else 276 { 277 buffer = new MemoryStream(contentLength.Value); 278 } 279 280 cancellationToken.ThrowIfCancellationRequested(); 281 282 using (StreamContent copier = new StreamContent(owinRequest.Body)) 283 { 284 await copier.CopyToAsync(buffer); 285 } 286 287 // Provide the non-disposing, buffered stream to later OWIN components (set to the stream\'s beginning). 288 buffer.Position = 0; 289 owinRequest.Body = buffer; 290 291 // For MemoryStream, Length is guaranteed to be an int. 292 return new ByteArrayContent(buffer.GetBuffer(), 0, (int)buffer.Length); 293 } 294 295 private static HttpRequestMessage CreateR以上是关于从System.Web.Http.Owin的HttpMessageHandlerAdapter看适配器模式的主要内容,如果未能解决你的问题,请参考以下文章
Azure api OAuth2 隐式流适用于 http 但不适用于 htt
java 示例登录Android上的Google登录活动,该活动检索用于服务器端身份验证的授权代码。见htt