1、内容协商的定义
1、内容协商(Content Negotiation)的定义是:当存在多个表示形式时,为给定响应选择最佳表示形式的过程
。也就是说Request
请求可接受xml
、json
、text
等格式的内容,选择哪种最合适,并返回给请求的过程就是内容协商
。
2、内容协商的主要依据是request请求中的:Accept
、Accept-Charset
、Accept-Encoding
、Accept-Language
。一个典型的request请求如下:
GET /webapidemo/api/products/getResponseMessage HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh,zh-CN;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
而内容协商就是根据request中提到的header,决定最后的响应方式。
最后响应如下:
HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Date: Tue, 13 Apr 2021 07:56:22 GMT
Content-Length: 82
通过对request中accept中最优的选择,进行了响应。这就是内容协商。
2、内容协商工作方式
1、管道从HttpConfiguration
对象中获取内容协商实例对象
、支持的媒体格式列表
2、管道调用内容协商实例对象
中Negotiate
方法,并传入:要序列化的对象(即返回给客户端的数据对象)
、Request
、媒体格式列表
3、内容协商实例对象
的Negotiate
方法,返回一个ContentNegotiationResult对象
。而这个对象中包含了:使用哪种格式化程序和要响应的媒体类型
下面的代码演示了如何直接调用内容协商
public HttpResponseMessage GetProduct(int id)
{
var product = new Product()
{ Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };
//通过HttpConfiguration对象获取内容协商对象
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, //需要序列化的对象
result.Formatter, //序列化格式器
result.MediaType.MediaType //媒体类型
)
};
}
本质上,内容协商就是一个序列化格式器的选择程序。
3、自定义内容协商
了解了内容协商,可以看一下如何自定义内容协商
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter; //使用系统自带的格式化器(本质是一个序列化程序)
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
//传入参数,然后实现Negotiate方法
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}
在配置文件中,再将默认的替换掉
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
这样就能够实现了自定义内容协商。
以上自定义的方法中,只是使用了系统自带的json序列化器(系统自带json和xml的序列器),若想自定义序列器,可参考MSDN的样例:自定义序列器MSDN的说明
4、总结
本质上内容协商是一个根据请求中的accept,对发送给客户端的数据,进行了指定格式的序列化的过程