springmvc 中关于“内容协商”的小记
0、第一步,你要先配置,对吧,像下边这样:
@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
/**
* Setup a simple strategy:
* 1. Only path extension is taken into account, Accept headers are ignored.
* 2. Return HTML by default when not sure.
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true)
.defaultContentType(MediaType.TEXT_HTML);
}
/**
* Create the CNVR. Get Spring to inject the ContentNegotiationManager created by the
* configurer (see previous method).
*/
@Bean
public ViewResolver contentNegotiatingViewResolver(
ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
return resolver;
}
}
1、其次,CNVR是指Content Negotiating View Resolver。既然是内容协商,那么CNVR就是其他视图解析器的老大咯!对于CNVR,它本身也是一个视图解析器,那么“order”这个概念对它是是不具备约束的。老大是通过ContentNegotiatingViewResolver 配置中的mediaTypes来指派具体由哪个视图解析器来resolve,spring通过你传过去的参数决定返回哪种mediaType。
2、你可以用一个List<ViewResolver> resolvers = new ArrayList<ViewResolver>();来作为视图解析器的集合,装入其他视图解析器,然后放入老大CNVR中进行统一调度。就像下边这样:
@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
// .. Other methods/declarations
/**
* Create the CNVR. Specify the view resolvers to use explicitly. Get Spring to inject
* the ContentNegotiationManager created by the configurer (see previous method).
*/
@Bean
public ViewResolver contentNegotiatingViewResolver(
ContentNegotiationManager manager) {
// Define the view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
XmlViewResolver r1 = new XmlViewResolver();
resolver.setLocation(new ServletContextResource(servletContext,
"/WEB-INF/spring/spreadsheet-views.xml"));
resolvers.add(r1);
InternalResourceViewResolver r2 = new InternalResourceViewResolver();
r2.setPrefix("WEB-INF/views");
r2.setSuffix(".jsp");
resolvers.add(r2);
// Create the CNVR plugging in the resolvers and the content-negotiation manager
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setViewResolvers(resolvers);
resolver.setContentNegotiationManager(manager);
return resolver;
}
}
3、如果这样了,你还不满足,毕竟spring提供的视图解析器就那么几种。你可以定义自己的视图解析器,比如json解析器:
@Bean
public class JsonViewResolver implements ViewResolver {
/**
* Get the view to use.
*
* @return Always returns an instance of {@link MappingJacksonJsonView}.
*/
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
MappingJacksonJsonView view = new MappingJacksonJsonView();
view.setPrettyPrint(true); // Lay the JSON out to be nicely readable
return view;
}
}
这样,你已经可以解析不少东西啦,看看:
4、看到这,我只想说 乖乖,现在我都玩restful啦,光一个试图解析算个毛啊。小伙伴们都在往注解的坑了跳,你要跟我一起跳吗?比如,玩玩@ResponseBody
其实为了更好的支持restful(包括修改数据的能力),我们是通过结合HTTP Message Converters以及用@controller注解的方法来玩的。views玩坏了,就只剩@ResponseBody当接盘侠了。或者,脚踏两条船,两者一起玩吧。艾玛,spring也真实灵活啊-。-
5、好,那我们继续来看下HttpMessageConverter
首先,我得说Content-type ,http请求中都会包含,请自行f12.
然后,说一下,MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息.Content-Type是返回消息中非常重要的内容,表示后面的文档属于什么MIME类型。
Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。原则上浏览器会根据Content-Type来决定如何显示返回的消息体内容。
然后,咱列个Content-Type的表:
常见的媒体格式类型如下:
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:
application/xhtml+xml :XHTML格式
application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式
application/json : JSON数据格式
application/pdf :pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
以上就是我们在日常的开发中,经常会用到的若干content-type的内容格式。
其中,springmvc会踩到的一个坑是:application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式) 。遇到 415 Unsupported Media Type的神坑时的我还根本反应过来。。竟然是converter配置出问题了。
小样,前端传个表格你unsupport是几个意思=。= god,请自行来一打FormHttpMessageConverter转转吧。
好啦,列一下常用的HttpMessageConverter:
ByteArrayHttpMessageConverter: 负责读取二进制格式的数据和写出二进制格式的数据;
StringHttpMessageConverter: 负责读取字符串格式的数据和写出二进制格式的数据;(字符串格式是什么啊 orz)
ResourceHttpMessageConverter:负责读取资源文件和写出资源文件数据;
FormHttpMessageConverter: 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据;
MappingJacksonHttpMessageConverter: 负责读取和写入json格式的数据;
SourceHttpMessageConverter: 负责读取和写入 xml 中javax.xml.transform.Source定义的数据;
Jaxb2RootElementHttpMessageConverter: 负责读取和写入xml 标签格式的数据;
AtomFeedHttpMessageConverter: 负责读取和写入Atom格式的数据;
RssChannelHttpMessageConverter: 负责读取和写入RSS格式的数据;
还不够?自行脑部!
6,科普完概念,我们看看流程,HttpMessageConverter请求信息转换器执行的:
默念五分钟,闭眼脑部:
跟我一起读----当用户发送请求后,@Requestbody 注解会读取请求body中的数据,默认的请求转换器HttpMessageConverter通过获取请求头Header中的Content-Type(上边我说过了)来确认请求头的数据格式,从而来为请求数据适配合适的转换器(有哪些,给我默写去)。例如contentType:applicatin/json,那么转换器会适配MappingJacksonHttpMessageConverter。响应时候的时候同理,@Responsebody注解会启用HttpMessageConverter,通过检测Header中Accept属性来适配的响应的转换器。