• 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,并根据请求的报送对其进行匹配(如果有)。

  • 相关阅读:
    浅谈Java的开放封闭原则
    Gson 和 Fastjson 你不知道的事
    mac 开发必备软件(不断update ing...)
    fastJson泛型如何转换
    springboot 学习笔记(二)--- properties 配置
    springboot 学习笔记(一)
    mac 安装MySQL
    mybatis 注解快速上手
    svn 冲突解决
    java画图输出到磁盘
  • 原文地址:https://www.cnblogs.com/aehyok/p/3481265.html
Copyright © 2020-2023  润新知