Message Handlers是一个接收HTTP Request返回HTTP Response的类,继承自HttpMessageHandler
通常,一些列的message handler被链接到一起。第一个handler收到http request做一些处理,然后将request传递到下一个handler。在某时刻,response被创并返回。这种模式被称为delegating hanlder
服务端消息处理
在服务端,WebAPI管道使用一些内建的message handlers
HttpServer
从主机获取requestHttpRoutingDispacher
基于路由分配requestHttpControllerDispacher
发送request到WebAPI的controller
可以在pipline中自定义message handlers,如图,展示了两个自定义的handler
自定义 Message Handlers
自定义message handler需要实现System.Net.Http.DelegatingHandler
接口,并重载SendAsync
方法。
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken);
典型的实现通过一些流程:
- 处理request message
- 调用
base.SendAsync
将request传递到内部的handler(inner handler) - 内部的handler返回一个response message(这步是异步的)
- 处理response并返回给调用者
一个简单的例子:
public class MessageHandler1 : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine("Process request");
// Call the inner handler.
var response = await base.SendAsync(request, cancellationToken);
Debug.WriteLine("Process response");
return response;
}
}
当然,也可以跳过inner handler,直接创建一个response(这种方式对于验证request很有用)
public class MessageHandler2 : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Create the response.
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("Hello!")
};
// Note: TaskCompletionSource creates a task that does not contain a delegate.
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response); // Also sets the task state to "RanToCompletion"
return tsc.Task;
}
}
添加Handler到Pipeline
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());
// Other code not shown...
}
}
Message Handler被调用的顺序与添加到MessageHandlers集合的顺序相同,由于是嵌套的response message消息传播的方向正好与此相反。
对于inner handlers我们不需要设置,WebAPI框架会自动连接。
Per-Route Message Handlers
既可以在 HttpConfiguration.MessageHandlers
集合中设置Handlers应用到globally范围,也可以在指定路由上添加一个message handler
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Route1",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Route2",
routeTemplate: "api2/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: new MessageHandler2()// per-route message handler
);
config.MessageHandlers.Add(new MessageHandler1()); // global message handler
}
}
通过以上配置,如果URI匹配"Route2",请求将被分配到MessageHandler2
。如下图所示:
注意:MessageHandler2
会替代默认的HttpControllerDispatcher
,匹配"Route2"的request将不能找到对应的controller,可以通过手动建立HttpControllerDispatcher
来解决。
// List of delegating handlers.
DelegatingHandler[] handlers = new DelegatingHandler[] {
new MessageHandler3()
};
// Create a message handler chain with an end-point.
var routeHandlers = HttpClientFactory.CreatePipeline(
new HttpControllerDispatcher(config), handlers);
config.Routes.MapHttpRoute(
name: "Route2",
routeTemplate: "api2/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: routeHandlers
);
使用Message Handler的例子
-
跳过inner handler 直接返回response
/// <summary> /// 跳过inner handler 直接返回response /// </summary> public class MyMessageHandler2:DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Create the response. var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Hello!") }; // Note: TaskCompletionSource creates a task that does not contain a delegate. var tsc = new TaskCompletionSource<HttpResponseMessage>(); tsc.SetResult(response); // Also sets the task state to "RanToCompletion" // return tsc.Task; return base.SendAsync(request,cancellationToken); } }
-
重写Request method
/// <summary> /// 重写Request method /// </summary> public class MethodOverrideHandler:DelegatingHandler { readonly string[] _methods = { "DELETE", "HEAD", "PUT" }; const string _header= "X-HTTP-Method-Override"; protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Method == HttpMethod.Post && request.Headers.Contains(_header)) { var method = request.Headers.GetValues(_header).FirstOrDefault(); if (_methods.Contains(method, StringComparer.InvariantCulture)) { request.Method = new HttpMethod(method); } } return base.SendAsync(request, cancellationToken); } }