zoukankan      html  css  js  c++  java
  • Web API中的消息处理程序(Message Handler)

    一、消息处理程序的概念

      信息处理程序(Message Handler)接收HTTP请求并返回一个HTTP响应的类。Message Handler继承 HttpMessageHandler 类。

      通常,一系列消息处理程序协同工作。第一个Message Handler接收HTTP请求,进行一些处理,并将请求提供给下一个Message Handler。在某些时候,响应被创建并返回到Message Handler,此模式称为委托处理程序(delegating handler)。

    二、服务器端消息处理程序

      在服务器端,Web API管道使用一些内置的消息处理程序:

    HttpServer                 从主机获取请求。
    HttpRoutingDispatcher      根据请求进行路由。
    HttpControllerDispatcher   将请求发送到Web API控制器。

      我们可以向管道添加自定义处理程序。消息处理程序更适合处理Http messages(比起控制器)。因为Message Handler可以:

    读取或修改请求标头。
    为响应添加响应标头。
    在请求到达控制器之前验证请求。

    此图显示了插入管道的两个自定义处理程序:

     

    三、自定义Message Handler

    第一步:创建Message Handler

      要编写自定义消息处理程序,请从 System.Net.Http.DelegatingHandler 派生并覆盖 SendAsync 方法。此方法具有以下签名:

    Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

      该方法将HttpRequestMessage作为输入,并异步返回HttpResponseMessage。典型的实现执行以下操作:

    1.处理请求消息。
    2.调用base.SendAsync将请求发送到内部处理程序。
    3.内部处理程序返回响应消息。(此步骤是异步的。)
    4.处理响应并将其返回给调用者。

    一个简单的栗子:

    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;
      }
    }

    调用 base.SendAsync 是异步的。如果处理程序在此调用后执行任何操作,请使用await关键字。

    委托处理程序也可以跳过inner handler

    一个跳过inner handler的栗子:

    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创建一个不包含委托的task
          var tsc = new TaskCompletionSource<HttpResponseMessage>();
          tsc.SetResult(response); // Also sets the task state to "RanToCompletion"
        return tsc.Task;
      }
    }

    如果delegate handler在创建响应时不调用base.SendAsync,则请求将跳过管道的其余部分。这对于验证请求的处理程序(创建错误响应)非常有用。

    第二步:将处理程序添加到管道

      要在服务器端添加消息处理程序,请将处理程序添加到 HttpConfiguration.MessageHandlers 集合中。

    public static class WebApiConfig
    {
      public static void Register(HttpConfiguration config)
      {
        config.MessageHandlers.Add(new MessageHandler1());
        config.MessageHandlers.Add(new MessageHandler2());
        // Other code not shown...
      }
    }

      消息处理程序的调用顺序与它们在MessageHandlers集合中的显示顺序相同。因为它们是嵌套的,所以响应消息反向传播。

    四、自定义消息处理程序实例

    4.1  添加自定义响应标头

    这是一个Message Handler,它为每个响应消息添加一个自定义标头:

    public class CustomHeaderHandler : DelegatingHandler
    {
        async protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
            response.Headers.Add("X-Custom-Header", "This is my custom header.");
            return response;
        }
    }

    首先,处理程序调用base.SendAsync将请求传递给inner handler。inner handler返回响应消息,但它使用Task <T>对象异步执行,在base.SendAsync异步完成之前,响应消息不可用。

    4.2  检查API密钥

    某些Web服务要求客户在其请求中包含API密钥。以下示例显示了消息处理程序如何检查有效API密钥的请求:

    public class ApiKeyHandler : DelegatingHandler
    {
      public string Key { get; set; }
      public ApiKeyHandler(string key)
      {
        this.Key = key;
      }
      //重写sendAsyc
      protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
      {
        if (!ValidateKey(request))
        {
          //没有通过则创建response,code为403
          var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
          var tsc = new TaskCompletionSource<HttpResponseMessage>();
          tsc.SetResult(response); 
          return tsc.Task;
        }
        return base.SendAsync(request, cancellationToken);
      }
      //验证密钥
      private bool ValidateKey(HttpRequestMessage message)
      {
        var query = message.RequestUri.ParseQueryString();
        string key = query["key"];
        return (key == Key);
      }
    }

      Message Handler在URI查询字符串中查找API密钥。如果查询字符串包含Key,则Message handler将请求传递给innner handler,没有key的话返回403。

    4.3  Per-Route Message Handler

      HttpConfiguration.MessageHandlers集合中的处理程序全局应用,有时候我们的Message Handler只针对特定的路径,如url中含有“admin”的,需要登陆后才能访问:

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
         //默认路由
            config.Routes.MapHttpRoute(
                name: "Route1",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
             );
         //路由中指定Message Handler
            config.Routes.MapHttpRoute(
                name: "Route2",
                routeTemplate: "api2/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional },
                handler: new MessageHandler2() // per-route message handler
         );
        //全局的Message Handler 
            config.MessageHandlers.Add(new MessageHandler1()); //     global message handler
        }
    }  

    在此示例中,如果请求URI与“Route2”匹配,则将分派请求MessageHandler2,如下图所示:

    这时MessageHandler2替换默认的HttpControllerDispatcher。这个栗子中MessageHandler2创建响应,匹配“Route2”的请求永远不会转到控制器。这使我们可以使用自己的自定义响应替换整个Web API控制器机制。

    我们也可以把路由的MessageHandler委托给 HttpControllerDispatcher ,通过HttpControllerDispatcher调度到控制器。

    以下代码显示了如何配置此路由:

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

    这里总结了Web API中的Message Handler中的概念和基本用法,了解更多用法可以查看官网

  • 相关阅读:
    IntelliJ IDEA 偏好设置
    Unix环境下的5中IO模型
    Hbase原理、基本概念、基本架构
    可参考的技术博客
    Hadoop生态系统介绍
    oracle 添加表分区和索引分区,修改索引分区默认表空间
    koa redis 链接
    Node-APN 开源推送服务
    NodeJs 笔记
    windows防火墙无法启动,服务不存在
  • 原文地址:https://www.cnblogs.com/wyy1234/p/9486492.html
Copyright © 2011-2022 走看看