首页 > 技术文章 > springmvc学习指南 之---第28篇 springmvc的controller 如何解析视图view? 如何解析json,html,jsp?

zytcomeon 2021-08-02 19:31 原文

writedby 张艳涛 通常我们使用controller,会标注RequestMapping,那么通常是返回值是String类型,那么return "一个字符串",这个字符串应该解析什么呢?

目录:

  1. @ResponseBody注解的用途
  2. 前端如何请求xml类型,json类型的返回值
  3. 后端的springmvc是如何解析的
  4. 扩展:如果前端发送的请求没有设置accept的值,那么返回的responsebody会为什么格式呢?
  5.   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

 

<javaBean>

<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设置为了

 

Accept:  application/json, text/plain, */*
springmvc处理为了json,那么spirngmvc背后逻辑是什么呢?
 

 

 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: 

推荐阅读