学习 Web API
  • 爱学习公众号

    关注爱学习公众号

  • 扫码手机阅读更方便

推荐阅读
  • 使用 Knockout.js 创建动态 UI

    本文内容 作者: Mike Wasson下载完成的项目使用 Knockout.js 创建动态 UI在本部分中,我们将使用 "挖空" 将功能添加到 "管理" 视图。"挖空" 是一种 Javascr

  • Web API 2 中的依赖项注入

    本教程演示如何在 ASP.NET Web API 控制器中注入依赖项。 本教程中使用的软件版本 Web API 2 Unity 应用程序块 实体框架6(版本5也适用)

  • 配置 Web API 2

    本文内容 作者: Mike Wasson本主题介绍如何配置 ASP.NET Web API。配置设置配置包含 ASP.NET 托管的 Web API配置包含 OWIN 自承载的 Web API全局 Web API 服务每控制器配置配置设置W

  • 创建管理员控制器

    本文内容 作者: Mike Wasson下载完成的项目添加管理控制器在本部分中,我们将添加一个 Web API 控制器,该控制器支持对产品的 CRUD (创建、读取、更新和删除)操作。 控制器将使用实体框架与数据库层进行通信。 只有管理员才能使用该控制器。

  • OData v3 对实体关系的支持

    大多数数据集定义实体之间的关系:客户具有订单;书作者:产品具有供应商。 使用 OData,客户端可在实体关系上导航。 在给定产品的情况下,您可以找到该供应商。 您还可以创建或删除关系。 例如,可以设置产品的供应商。 本教程演示如何在 ASP.NET Web API 中支持这些操作。 本教程基于使用 WEB API 2 创建 OData V3 终结点教程。 本教程中使用的软件版本 Web API

  • 向数据库添加一个新项

    本文内容 作者: Mike Wasson下载完成的项目在本部分中,你将添加用户创建新书籍的能力。 在 node.js 中,将以下代码添加到视图模型:self.authors = ko.observableArray();self.newBook = {

  • 在 Web API 2 中启用跨域请求

    浏览器安全策略阻止 Web 页向另一个域发出 AJAX 请求。 此限制称为相同源策略,可防止恶意站点读取另一个站点中的敏感数据。 但是,有时你可能希望让其他站点调用你的 web API。 跨源资源共享(CORS)是一种 W3C 标准,允许服务器放松相同的源策略。 使用 CORS,服务器可以在显式允许某些跨域请求时拒绝其他跨域请求。 CORS 比早期技术(如JSONP)更安全且更灵活。 本教程演示

  • Web API 2 入门 (C#)

    本文内容 作者: Mike Wasson下载完成的项目在本教程中,你将使用 ASP.NET Web API 创建返回产品列表的 Web API。HTTP 并非仅用于为网页提供服务。 HTTP 也是一个功能强大的平台,用于构建公开服务和数据的 Api。 HT

  • 创建 Web API 帮助页

    本文内容 作者: Mike Wasson本教程中的代码演示了如何在 ASP.NET 4.x 中创建 ASP.NET Web API 的帮助页。创建 web API 时,创建帮助页通常很有用,因为其他开发人员将知道如何调用 API。 您可以手动创建所有文档,但

  • 创建主页面

    本文内容 作者: Mike Wasson下载完成的项目创建主页面在本部分中,你将创建主应用程序页。 此页将比管理页更复杂,因此,我们将通过几个步骤来进行处理。 在此过程中,您将看到一些更高级的挖空技术。 下面是页面的基本布局:"产品&quo

Web API 中的 HTTP 消息处理程序

作者: Mike Wasson

消息处理程序是接收 http 请求并返回 http 响应的类。 消息处理程序派生自抽象HttpMessageHandler类。

通常,一系列消息处理程序链接在一起。 第一个处理程序接收 HTTP 请求,进行一些处理,并向下一个处理程序提供请求。 在某个时间点,将创建响应,并在链中返回。 此模式称为委托处理程序。

服务器端消息处理程序

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

  • HttpServer获取来自主机的请求。
  • HttpRoutingDispatcher根据路由调度请求。
  • System.web.http.exceptionhandling.exceptioncatchblocks.httpcontrollerdispatcher.sendasync将请求发送到 Web API 控制器。

您可以向管道添加自定义处理程序。 消息处理程序适用于在 HTTP 消息级别(而不是控制器操作)上操作的交叉切削问题。 例如,消息处理程序可以:

  • 读取或修改请求标头。
  • 向响应添加响应标头。
  • 在请求到达控制器之前对其进行验证。

此关系图显示插入到管道中的两个自定义处理程序:

Note

在客户端,HttpClient 还使用消息处理程序。 有关详细信息,请参阅HttpClient 消息处理程序

自定义消息处理程序

若要编写自定义消息处理程序,请从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;
    }
}

Note

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

委托处理程序还可以跳过内部处理程序并直接创建响应:

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

如果委托处理程序在没有调用 base.SendAsync的情况下创建响应,则该请求将跳过管道的其余部分。 这对于验证请求的处理程序(创建错误响应)很有用。

向管道添加处理程序

若要在服务器端添加消息处理程序,请将该处理程序添加到HttpConfiguration集合。 如果使用了 "ASP.NET MVC 4 Web 应用程序" 模板来创建项目,可以在webapiconfig.cs类中执行此操作:

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集合中出现的顺序相同。 由于它们是嵌套的,因此响应消息以其他方向传输。 也就是说,最后一个处理程序是获取响应消息的第一个处理程序。

请注意,不需要设置内部处理程序;Web API 框架会自动连接消息处理程序。

如果你是自承载,请创建HttpSelfHostConfiguration类的实例,并将该处理程序添加到MessageHandlers集合中。

var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());

现在,让我们看一些自定义消息处理程序示例。

示例: X HTTP-方法-Override

X HTTP 方法-重写是非标准的 HTTP 标头。 它适用于不能发送某些 HTTP 请求类型的客户端,例如 PUT 或 DELETE。 相反,客户端发送 POST 请求,并将 X HTTP 方法重写标头设置为所需的方法。 例如:

X-HTTP-Method-Override: PUT

下面是添加对 X HTTP 方法重写的支持的消息处理程序:

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)
    {
        // Check for HTTP POST with the X-HTTP-Method-Override header.
        if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
        {
            // Check if the header value is in our methods list.
            var method = request.Headers.GetValues(_header).FirstOrDefault();
            if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
            {
                // Change the request method.
                request.Method = new HttpMethod(method);
            }
        }
        return base.SendAsync(request, cancellationToken);
    }
}

SendAsync方法中,处理程序检查请求消息是 POST 请求,还是包含 X HTTP 方法-重写标头。 如果是,它将验证标头值,然后修改请求方法。 最后,处理程序调用 base.SendAsync 将消息传递到下一个处理程序。

当请求到达system.web.http.exceptionhandling.exceptioncatchblocks.httpcontrollerdispatcher.sendasync类时, system.web.http.exceptionhandling.exceptioncatchblocks.httpcontrollerdispatcher.sendasync将根据更新的请求方法路由请求。

示例:添加自定义响应标头

下面是一个消息处理程序,它将自定义标头添加到每个响应消息:

// .Net 4.5
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 将请求传递给内部消息处理程序。 内部处理程序返回响应消息,但它使用任务<t> 对象以异步方式执行此操作。 直到 base.SendAsync 异步完成后,响应消息才可用。

此示例使用await关键字在 SendAsync 完成后以异步方式执行工作。 如果目标为 .NET Framework 4.0,请使用任务<t> 。System.threading.tasks.task.continuewith方法:

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

示例:检查 API 密钥

某些 web 服务要求客户端在其请求中包含 API 密钥。 下面的示例演示消息处理程序如何检查对有效 API 密钥的请求:

public class ApiKeyHandler : DelegatingHandler
{
    public string Key { get; set; }

    public ApiKeyHandler(string key)
    {
        this.Key = key;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!ValidateKey(request))
        {
            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);
    }
}

此处理程序在 URI 查询字符串中查找 API 密钥。 (在本示例中,我们假定密钥是一个静态字符串。 实际实现可能会使用更复杂的验证。)如果查询字符串包含该键,则处理程序将该请求传递给内部处理程序。

如果请求没有有效的密钥,则处理程序将创建状态为403,禁止的响应消息。 在这种情况下,处理程序不会调用 base.SendAsync,因此内部处理程序绝不会接收请求,也不会执行控制器。 因此,控制器可以假定所有传入请求都有有效的 API 密钥。

Note

如果 API 密钥仅适用于某些控制器操作,请考虑使用操作筛选器而不是消息处理程序。 操作筛选器在执行 URI 路由之后运行。

按路由消息处理程序

HttpConfiguration集合中的处理程序全局应用。

此外,还可以在定义路由时向特定路由添加消息处理程序:

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 替换默认system.web.http.exceptionhandling.exceptioncatchblocks.httpcontrollerdispatcher.sendasync 在此示例中,MessageHandler2 将创建响应,与 "Route2" 匹配的请求永远不会发送到控制器。 这使你可以将整个 Web API 控制器机制替换为你自己的自定义终结点。

或者,每个路由消息处理程序可以委托给system.web.http.exceptionhandling.exceptioncatchblocks.httpcontrollerdispatcher.sendasync,后者随后会调度到控制器。

下面的代码演示如何配置此路由:

// 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
);
关于我们 免责声明 联系我们
Copyright © 2020 爱学习网 浙ICP备18049359号 网站地图 Google地图