6.6.五、生产者、消费者限定html
首先让咱们看一下经过HTTP协议传输的媒体类型及如何表示媒体类型:java
1、Media Type:web
互联网媒体类型,通常就是咱们所说的MIME类型,用来肯定请求的内容类型或响应的内容类型。spring
常见媒体类型:chrome
text/html : HTML格式 text/plain :纯文本格式 text/xml :XML格式json
image/gif :gif图片格式 image/jpeg :jpg图片格式 image/png:png图片格式浏览器
application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)。tomcat
multipart/form-data : 当你须要在表单中进行文件上传时,就须要使用该格式;服务器
application/xhtml+xml :XHTML格式 application/xml : XML数据格式 mvc
application/atom+xml :Atom XML聚合格式 application/json : JSON数据格式
application/pdf :pdf格式 application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)。
在如tomcat服务器的 “conf/web.xml”中指定了扩展名到媒体类型的映射,在此咱们能够看到服务器支持的媒体类型。
2、Content-Type:内容类型,即请求/响应的内容区数据的媒体类型;
2.一、请求头的内容类型,表示发送到服务器的内容数据的媒体类型;
request中设置请求头“Content-Type: application/x-www-form-urlencoded”表示请求的数据为key/value数据;
(一、控制器cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeController
showForm功能处理方式:展现表单,且form的enctype="application/x-www-form-urlencoded",在提交时请求的内容类型头为“Content-Type:application/x-www-form-urlencoded”;
request1功能处理方法:只对请求头为“Content-Type:application/x-www-form-urlencoded”的请求进行处理(即消费请求内容区数据);
request.getContentType():能够获得请求头的内容区数据类型(即Content-Type头的值)
request.getCharacterEncoding():如“Content-Type:application/json;charset=GBK”,则获得的编码为“GBK”,不然若是你设置过滤器(CharacterEncodingFilter)则获得它设置的编码,不然返回null。
request.getParameter():由于请求的内容区数据为application/x-www-form-urlencoded格式的数据,所以咱们能够经过request.getParameter()获得相应参数数据。
request中设置请求头“Content-Type:application/json;charset=GBK”表示请求的内容区数据为json类型数据,且内容区的数据以GBK进行编码;
(一、控制器cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeController
request2功能处理方法:只对请求头为“Content-Type:application/json”的进行请求处理(即消费请求内容区数据);
request.getContentLength():能够获得请求头的内容区数据的长度;
request.getCharacterEncoding():如“Content-Type:application/json;charset=GBK”,则获得的编码为“GBK”,不然若是你设置过滤器(CharacterEncodingFilter)则获得它设置的编码,不然返回null。
咱们获得json的字符串形式后就能很简单的转换为JSON相关的对象。
(二、客户端发送json数据请求
此处咱们使用Spring提供的Http客户端API SimpleClientHttpRequestFactory建立了请求并设置了请求的Content-Type和编码并在响应体中写回了json数据(即生产json类型的数据),此处是硬编码,实际工做可使用json-lib等工具进行转换。
具体代码在cn.javass.chapter6.web.controller.consumesproduces.contenttype.RequestContentTypeClient。
2.二、响应头的内容类型,表示发送到客户端的内容数据类型,和请求头的内容类型相似,只是方向相反。
<!--[endif]-->
如上所示,经过response.setContentType("text/html;charset=utf-8") 告诉客户端响应体媒体类型为html,编码为utf-8,你们能够经过chrome工具查看响应头为“Content-Type:text/html;charset=utf-8”,还一个“Content-Length:36”表示响应体大小。
代码在cn.javass.chapter6.web.controller.consumesproduces.contenttype.ResponseContentTypeController。
如上代码能够看出Content-Type能够指定请求/响应的内容体的媒体格式和可选的编码方式。如图6-9
①客户端—发送请求—服务器:客户端经过请求头Content-Type指定内容体的媒体类型(即客户端此时是生产者),服务器根据Content-Type消费内容体数据(即服务器此时是消费者);
②服务器—发送请求—客户端:服务器生产响应头Content-Type指定的响应体数据(即服务器此时是生产者),客户端根据Content-Type消费内容体数据(即客户端此时是消费者)。
问题:
①服务器端能够经过指定【headers = "Content-Type=application/json"】来声明可处理(可消费)的媒体类型,即只消费Content-Type指定的请求内容体数据;
②客户端如何告诉服务器端它只消费什么媒体类型的数据呢?即客户端接受(须要)什么类型的数据呢?服务器应该生产什么类型的数据?此时咱们能够请求的Accept请求头来实现这个功能。
3、Accept:用来指定什么媒体类型的响应是可接受的,即告诉服务器我须要什么媒体类型的数据,此时服务器应该根据Accept请求头生产指定媒体类型的数据。
2.一、json数据
(一、服务器端控制器
服务器根据请求头“Accept=application/json”生产json数据。
(二、客户端端接收服务器端json数据响应
使用浏览器测试(Ajax场景使用该方式)
请求地址为:http://localhost:9080/springmvc-chapter6/response/ContentType,且把修改请求头Accept改成“Accept=application/json”:
你们能够下载chrome的JSONView插件来以更好看的方式查看json数据,安装地址:https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc
使用普通客户端测试(服务器之间通讯可以使用该方式)
request.getHeaders().set("Accept", "application/json"):表示客户端只接受(即只消费)json格式的响应数据;
response.getHeaders():能够获得响应头,从而能够获得响应体的内容类型和编码、内容长度。
2.二、xml数据
(一、服务器端控制器
和生产json数据惟一不一样的两点:请求头为“Accept=application/xml”,响应体数据为xml。
(二、客户端端接收服务器端xml数据响应
使用浏览器测试(Ajax场景使用该方式)
请求地址为:http://localhost:9080/springmvc-chapter6/response/ContentType,且把修改请求头Accept改成“Accept=application/xml”,和json方式相似,此处再也不重复。
使用普通客户端测试(服务器之间通讯可以使用该方式)
request.getHeaders().set("Accept", "application/xml"):表示客户端只接受(即只消费)xml格式的响应数据;
response.getHeaders():能够获得响应头,从而能够获得响应体的内容类型和编码、内容长度。
许多开放平台,都提供了同一种数据的多种不一样的表现形式,此时咱们能够根据Accept请求头告诉它们咱们须要什么类型的数据,他们根据咱们的Accept来判断须要返回什么类型的数据。
实际项目使用Accept请求头是比较麻烦的,如今大多数开放平台(国内的新浪微博、淘宝、腾讯等开放平台)使用以下两种方式:
扩展名:如response/ContentType.json response/ContentType.xml方式,使用扩展名表示须要什么类型的数据;
参数:如response/ContentType?format=json response/ContentType?format=xml,使用参数表示须要什么类型的数据;
也就是说,目前咱们可使用如上三种方式实现来告诉服务器咱们须要什么类型的数据,但麻烦的是如今有三种实现方式,难道咱们为了支持三种类型的数据就要分别进行三种实现吗?固然不要这么麻烦,后续咱们会学ContentNegotiatingViewResolver,它能帮助咱们作到这一点。
生产者消费者流程,如图6-10:
从图6-10能够看出:
请求阶段:客户端是生产者【生产Content-Type媒体类型的请求内容区数据】,服务器是消费者【消费客户端生产的Content-Type媒体类型的请求内容区数据】;
响应阶段:服务器是生产者【生产客户端请求头参数Accept指定的响应体数据】,客户端是消费者【消费服务器根据Accept请求头生产的响应体数据】。
如上生产者/消费者写法没法很好的体现咱们分析的生产者/消费者模式,Spring3.1为生产者/消费者模式提供了简化支持,接下来咱们学习一下如何在Spring3.1中来实现生产者/消费者模式吧。
Spring3.1开始支持消费者、生产者限定,并且必须使用以下HandlerMapping和HandlerAdapter才支持:
1、功能处理方法是消费者
@RequestMapping(value = "/consumes", consumes = {"application/json"}):此处使用consumes来指定功能处理方法能消费的媒体类型,其经过请求头的“Content-Type”来判断。
此种方式相对使用@RequestMapping的“headers = "Content-Type=application/json"”更能代表你的目的。
服务器控制器代码详解cn.javass.chapter6.web.controller.consumesproduces.ConsumesController;
客户端代码相似于以前的Content-Type中的客户端,详见ConsumesClient.java代码。
2、功能处理方法是生产者
@RequestMapping(value = "/produces", produces = "application/json"):表示将功能处理方法将生产json格式的数据,此时根据请求头中的Accept进行匹配,如请求头“Accept:application/json”时便可匹配;
@RequestMapping(value = "/produces", produces = "application/xml"):表示将功能处理方法将生产xml格式的数据,此时根据请求头中的Accept进行匹配,如请求头“Accept:application/xml”时便可匹配。
此种方式相对使用@RequestMapping的“headers = "Accept=application/json"”更能代表你的目的。
服务器控制器代码详解cn.javass.chapter6.web.controller.consumesproduces.ProducesController;
客户端代码相似于以前的Content-Type中的客户端,详见ProducesController.java代码。
当你有以下Accept头:
①Accept:text/html,application/xml,application/json
将按照以下顺序进行produces的匹配 ①text/html ②application/xml ③application/json
②Accept:application/xml;q=0.5,application/json;q=0.9,text/html
将按照以下顺序进行produces的匹配 ①text/html ②application/json ③application/xml
q参数为媒体类型的质量因子,越大则优先权越高(从0到1)
③Accept:*/*,text/*,text/html
将按照以下顺序进行produces的匹配 ①text/html ②text/* ③*/*
即匹配规则为:最明确的优先匹配。
代码详见ProducesPrecedenceController一、ProducesPrecedenceController二、ProducesPrecedenceController3。
Accept详细信息,请参考http://tools.ietf.org/html/rfc2616#section-14.1。
3、窄化时是覆盖 而 非继承
如类级别的映射为 @RequestMapping(value="/narrow", produces="text/html"),方法级别的为@RequestMapping(produces="application/xml"),此时方法级别的映射将覆盖类级别的,所以请求头“Accept:application/xml”是成功的,而“text/html”将报406错误码,表示不支持的请求媒体类型。
详见cn.javass.chapter6.web.controller.consumesproduces.NarrowController。
只有生产者/消费者 模式 是 覆盖,其余的使用方法是继承,如headers、params等都是继承。
4、组合使用是“或”的关系
@RequestMapping(produces={"text/html", "application/json"}) :将匹配“Accept:text/html”或“Accept:application/json”。
5、问题
消费的数据,如JSON数据、XML数据都是由咱们读取请求的InputStream并根据须要本身转换为相应的模型数据,比较麻烦;
生产的数据,如JSON数据、XML数据都是由咱们本身先把模型数据转换为json/xml等数据,而后输出响应流,也是比较麻烦的。