zoukankan      html  css  js  c++  java
  • Asp.Net Web API 2第十四课——Content Negotiation(内容协商)

    前言

    阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html

    本文描述ASP.NET Web API如何实现内容协商。

    HTTP规范(RFC 2616)将内容协商定义为“在有多个表现可用时,为一个给定的响应选择最佳表现的过程”。在HTTP中内容协商的主要机制是以下请求报头:

    • Accept:响应可接收的媒体类型,如“application/json”、“application/xml”,或者自定义媒体类型,如“application/vnd.example+xml”。
    • Accept-Charset:可接收的字符集,如“UTF-8”或“ISO 8859-1”。
    • Accept-Encoding:可接收的内容编码,如“gzip”。
    • Accept-Language:优先选用的自然语言,如“en-us”。

    服务器也可以查看HTTP请求的其它选项。例如,如果该请求含有一个X-Requested-With报头,它指示这是一个AJAX请求,在没有Accept报头的情况下,服务器可能会默认使用JSON。

    本文将考察Web API如何使用Accept和Accept-Charset报头。(目前,还没有对Accept-Encoding或Accept-Language的内建支持。)

    Serialization——序列化

    如果Web API控制器返回一个CLR类型的响应,(请求处理)管线会对返回值进行序列化,并将其写入HTTP响应体。

    例如,考虑以下控制器动作:

    复制代码
    public Product GetProduct(int id)
    {
        var item = _products.FirstOrDefault(p => p.ID == id);
        if (item == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        return item; 
    }
    复制代码

    客户端可能会发送这样的HTTP请求:

    GET http://localhost.:21069/api/products/1 HTTP/1.1
    Host: localhost.:21069
    Accept: application/json, text/javascript, */*; q=0.01

    服务器可能会发送以下响应:

    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    Content-Length: 57
    Connection: Close
    
    {"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

    在这个例子中,客户端请求(指定)了JSON、Javascript、或“任意格式(*/*)”。服务器以一个Product对象的JSON表示作出了响应。注意,响应中的Content-Type报头已被设置成“application/json”。

    控制器也可以返回一个HttpResponseMessage对象。为了指定响应体的CLR对象,要调用CreateResponse扩展方法:

    复制代码
    public HttpResponseMessage GetProduct(int id)
    {
        var item = _products.FirstOrDefault(p => p.ID == id);
        if (item == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        return Request.CreateResponse(HttpStatusCode.OK, product);
    }
    复制代码

    该选项让你能够对响应细节进行更多的控制。你可以设置状态码、添加HTTP报头等等。

    对资源进行序列化的对象叫做媒体格式化器。媒体格式化器派生于MediaTypeFormatter类。Web API提供了XML和JSON的媒体格式化器,因而你可以创建自定义的格式化器,以支持其它媒体类型。更多关于编写自定义格式化器的信息 http://www.cnblogs.com/aehyok/p/3460164.html

    内容协商的工作机制

    首先,管线会获取HttpConfiguration对象的IContentNegotiator服务。它也会得到HttpConfiguration.Formatters集合的媒体格式化器列表。

    接着,管线会调用IContentNegotiatior.Negotiate,在其中传递:

    • 要序列化的对象类型
    • 媒体格式化器集合
    • HTTP请求

    Negotiate方法返回两个信息片段:

    • 要使用的格式化器
    • 用于响应的媒体类型

    如果未找到格式化器,方法返回null,而客户端会接收到一个HTTP的406(不可接收的)错误。

    以下代码展示了控制器如何才能够直接调用内容协商:

    复制代码
    public HttpResponseMessage GetProduct(int id)
    {
        var product = new Product() 
            { Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };
    
        IContentNegotiator negotiator = this.Configuration.Services.GetContentNegotiator();
    
        ContentNegotiationResult result = negotiator.Negotiate(
            typeof(Product), this.Request, this.Configuration.Formatters);
        if (result == null)
        {
            var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
            throw new HttpResponseException(response));
        }
    
        return new HttpResponseMessage()
        {
            Content = new ObjectContent<Product>(
                product,                // What we are serializing(序列化什么)
                result.Formatter,           // The media formatter(媒体格式化器
                result.MediaType.MediaType  // The MIME type(MIME类型)
            )
        };
    }
    复制代码

    上述代码等价于管线的自动完成。

    默认的内容协定

    DefaultContentNegotiator类提供了IContentNegotiator的默认实现。它使用了几个选择格式化器的条件。

    首先,格式化器必须能够对类型进行序列化,这是通过MediaTypeFormatter.CanWriteType来检验的。

    其次,内容协商器要考查每个格式化器,并评估此格式化器与HTTP请求的匹配好坏。为了评估匹配情况,内容协商器要对此格式化器考察两样东西:

    • SupportedMediaTypes集合,它含有一个可支持的媒体类型的列表。内容协商器尝试根据请求的Accept报头对这个列表进行匹配。注意,Accept报头可以包括范围。例如,“text/plain”可匹配“text/*”或“*/*”
    • MediaTypeMappings集合,它含有对象一个MediaTypeMapping的对象列表。MediaTypeMapping类提供了一种泛型方式,以匹配带有媒体类型的HTTP请求。例如,它可以将一个自定义的HTTP报头映射到一个特定的媒体类型。

    如果有多个匹配,带有最高质量因子的匹配获胜。例如:

    Accept: application/json, application/xml; q=0.9, */*; q=0.1

    在这个例子中,application/json具有隐含的质量因子1.0,因此它优于application/xml。

    如果未找到匹配,内容协商器会尝试匹配请求体的媒体类型(有请求体时)。例如,如果请求含有JSON数据,内容协商器会找到JSON格式化器。

    如果仍无匹配,内容协商器便简单地捡取能够对类型进行序列化的第一个格式化器。

    选择字符编码

     在选择格式化器之后,内容协商器会选择最佳字符编码。通过考察格式化器的SupportedEncodings,并根据请求的报送对其进行匹配(如果有)。

  • 相关阅读:
    PHP 开发 APP 接口 学习笔记与总结
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 43 字符串相乘
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 42 接雨水
    Java实现 LeetCode 41 缺失的第一个正数
    Java实现 LeetCode 41 缺失的第一个正数
    Java实现 LeetCode 41 缺失的第一个正数
  • 原文地址:https://www.cnblogs.com/dwuge/p/5331717.html
Copyright © 2011-2022 走看看