springmvc学习指南 之---第28篇 springmvc的controller 如何解析视图view? 如何解析json,html,jsp?
writedby 张艳涛 通常我们使用controller,会标注RequestMapping,那么通常是返回值是String类型,那么return "一个字符串",这个字符串应该解析什么呢?
目录:
- @ResponseBody注解的用途
- 前端如何请求xml类型,json类型的返回值
- 后端的springmvc是如何解析的
- 扩展:如果前端发送的请求没有设置accept的值,那么返回的responsebody会为什么格式呢?
- consumes = "application/json", produces = "application/json;charset=UTF-8"两个注解的讲解
@ResponseBody注解的用途
1.1基本用法举例,摘自spring-mvc-showcase
@Controller public class SimpleController { @RequestMapping("/simple") public @ResponseBody String simple() { return "Hello world!"; } }
1.2前端请求
<div id="simple"> <h2>Simple</h2> <p> See the <code>org.springframework.samples.mvc.simple</code> package for the @Controller code </p> <ul> <li> <a id="simpleLink" class="textLink" href="<c:url value="/simple" />">GET /simple</a> </li> <li> <a id="simpleRevisited" class="textLink" href="<c:url value="/simple/revisited" />">GET /simple/revisited</a> </li> </ul> </div>
$("a.textLink").click(function(){ var link = $(this); $.ajax({ url: link.attr("href"), dataType: "text", success: function(text) { MvcUtil.showSuccessResponse(text, link); }, error: function(xhr) { MvcUtil.showErrorResponse(xhr.responseText, link); }}); return false; });
1.3返回类型
如果看respnose headers 和responsebody信息如下:
HTTP/1.1 200
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 12
Date: Mon, 02 Aug 2021 09:49:35 GMT
Hello world!
1.4以上就解释了@responsebody的作用是将controller的返回字符串 添加为response的body里面了,还要注意到context-Type为text/plain了
1.5上述没有说Accept的用途,accept的用途是指定response返回数据的格式,spring会根据accapt的格式设置body的内容格式
如果对上面的发送ajax做个手脚
$("a.textLink").click(function(){ var link = $(this); $.ajax({ url: link.attr("href"), dataType: "text", beforeSend: function(req) { req.setRequestHeader("Accept", "application/xml"); }, success: function(text) { MvcUtil.showSuccessResponse(text, link); }, error: function(xhr) { MvcUtil.showErrorResponse(xhr.responseText, link); }}); return false; });
那么看返回的是response
Content-Type: application/xml Content-Length: 12 Date: Mon, 02 Aug 2021 09:59:06 GMT
看到了返回的是一个xml类型的信息,其字符串还是Hello World!
这个例子不是很明显,accept启的作用,
$("a.writeXmlLink").click(function() { var link = $(this); $.ajax({ url: link.attr("href"), beforeSend: function(req) { if (!this.url.match(/\.xml$/)) { req.setRequestHeader("Accept", "application/xml"); } }, success: function(xml) { MvcUtil.showSuccessResponse(MvcUtil.xmlencode(xml), link); }, error: function(xhr) { MvcUtil.showErrorResponse(xhr.responseText, link); } }); return false; });
json的
$("a.writeJsonLink").click(function() { var link = $(this); $.ajax({ url: this.href, beforeSend: function(req) { if (!this.url.match(/\.json$/)) { req.setRequestHeader("Accept", "application/json"); } }, success: function(json) { MvcUtil.showSuccessResponse(JSON.stringify(json), link); }, error: function(xhr) { MvcUtil.showErrorResponse(xhr.responseText, link); }}); return false; });
在看下controllre内容
@RequestMapping(value="/xml", method=RequestMethod.GET) public @ResponseBody JavaBean writeXml() { return new JavaBean("bar", "apple"); } ////===== @RequestMapping(value="/json", method=RequestMethod.GET) public @ResponseBody JavaBean writeJson() { return new JavaBean("bar", "apple"); }
第一个返回值为
HTTP/1.1 200 Content-Type: application/xml Transfer-Encoding: chunked Date: Mon, 02 Aug 2021 10:05:29 GMT
<foo>bar</foo>
<fruit>apple</fruit>
第二个
HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 Transfer-Encoding: chunked Date: Mon, 02 Aug 2021 10:17:07 GMT
{"foo":"bar","fruit":"apple"}
通过设置了不同的accept头,springmvc区别对待,返回了不同的值
1.6以上是通过ajax来发送请求的,那么vue呢?不好意,我对前端也是有研究的
// 全部管理机构
asyncSetList({ commit }) {
// 发请求获取list数据
return new Promise((resolve, reject) => {
const params = {
url: 'api/application/CommonQueryAPI/queryLdCom'
}
apiByPost(params).then(res => {
if (res.status === 0) {
// 异步操作mutation函数赋值给state.ManageComlist
commit('setList', res.data)
resolve(res)
}
}).catch(error => {
reject(error)
})
})
},
export function apiByPost(params) { return request({ url: process.env.NODE_ENV === 'production' ? serviceSplit(params.url).substring(3) : serviceSplit(params.url), method: 'post', data: params.data }) } export function apiByPost2(params) { return request({ url: process.env.NODE_ENV === 'production' ? serviceSplit(params.url).substring(3) : serviceSplit(params.url), method: 'post', data: params.data, headers: { 'Content-Type': 'multipart/form-data' } }) }
那么看请求头
如图所示,axios将accept设置为了
2springmvc解析response的背后逻辑
2.1debug进入代码
2.2进入 selectHandler 方法
对于返回值的处理器有17个,通过 handler.supportsReturnType(returnType) 来确定1-17那个处理满足要求就用哪个
就是比较controller的返回值类型和handler是否匹配,看下面能知道返回值类型为 public class JavaBean
现在到了序号为13的处理器,看内部
就是看有没有@Responsebody注解,那么咱们的方法上是有的,那么就使用这个处理器
2.3进入 writeWithMessageConverters
request要求的格式为
springmvc能产出的格式为
逻辑为
以此找到合适的就是json,那么摔死messageConverter消息转换器来转换,
有9个转换器
现在在第8个解析器的时候,
有俩个条件,一看JavaBean类上有没有注解
package org.springframework.samples.mvc.mapping; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class JavaBean { private String foo = "bar"; private String fruit = "apple"; public String getFoo() { return foo; } public void setFoo(String foo) { this.foo = foo; } public String getFruit() { return fruit; } public void setFruit(String fruit) { this.fruit = fruit; } @Override public String toString() { return "JavaBean {foo=[" + foo + "], fruit=[" + fruit + "]}"; } }
现在是有注解,在看
在看xml的类型和传入对比的一样吗
现在是不一样,那么就返回对比不成功
支持类型一共有3
最后的一个handler,json解析,支持类型有2个
答案马上揭晓了
准备写入
显示父类的,设置了contextType
接着进入子类 AbstractJackson2HttpMessageConverter
3如果不设置accept的值,默认情况(此为ajax的默认,不是axio的默认)
代码如下
$("a.writeJsonLink").click(function() { var link = $(this); $.ajax({ url: this.href, beforeSend: function(req) { if (!this.url.match(/\.json$/)) { // req.setRequestHeader("Accept", "application/json"); } }, success: function(json) { MvcUtil.showSuccessResponse(JSON.stringify(json), link); }, error: function(xhr) { MvcUtil.showErrorResponse(xhr.responseText, link); }}); return false; });
查看请求结果
说明了什么?说明了,spring并不只能的给你返回来xml格式的responsebody,
此时如果浏览器如果按照xml解析,是解析不出来内容的,原因是什么呢?
是在messageConverters对象是一个ArrayList其中是由顺序的,其中xml解析在json解析之前
如图
所以在accept为"*/*"的时候,就是按照转换器那个优先那个先执行
4 consumes = "application/json", produces = "application/json;charset=UTF-8"两个注解
4.1用例
@RequestMapping(value = "/json", method = RequestMethod.GET , consumes = "application/json", produces = "application/json;charset=UTF-8") public @ResponseBody JavaBean writeJson() { return new JavaBean("bar", "apple"); }
4.2这两个注解的作用
第一个,consumes = "application/json" 作用要和来的请求reqeust进行比较,如果你的request的ContextType!=null 且和注解consumes一致就通过,否则不能匹配
第二个,produces是将设置responsebody的格式,就是显性的设置,效果和accept效果是一致的,如果前端不设置accept,那么后端可以设置produces,当然也可以不设置,
使用pringmvc默认策略(在不出错的情况下)
4.3对consumes="application/json"设置后,handlermapping的匹配逻辑
关键在这里
逻辑是查看request 中有没有设置contentType的值,本例是没有,那么
contentType= application/octet-stream
查看controller上的consumes="xxx,xxx"中有没有octet-stream,如果没有则不能匹配,就根据给出的url找不到controller
end: