Spring MVC 处理json类型请求及返回Json格式的几种方式

2022-07-09 14:39:28

Spring MVC 处理json类型请求及返回Json格式的几种方式

一、@RequestBody注解

它能帮我们解析客户端(移动设备、浏览器等)发送过来的json数据,并封装到实体类中。
常用的http请求MIME类型有application/json、text/html、text/xml、image/jpeg等,这些都是对应固定格式的Content-Type类型。
在网页中表单form元素的语法中,EncType表明提交数据的格式 用 Enctype 属性指定将数据回发到服务器时浏览器使用的编码类型。
例如:

  • application/x-www-form-urlencoded: 窗体数据被编码为名称/值对。这是默认(标准)的编码格式。当form的method为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…),然后把这个字串append到url后面,用?分割,加载这个新的url。
  • multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分,这个一般文件上传时用。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server。 如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。 但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。
  • text/plain: 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符这个不在form表单的类型里。
    重点来了~~~
    @ReqeustBody:
    常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如:application/json或者是application/xml等,常常用来其来处理application/json类型。
    注意:@requestBody接收的是前端传过来的json字符串,而不是JSON对象,在http协议中只能传输字符串,对象只能在前端或者后端语言中存在,所以前端的Json对象必须使用javascript提供的JSON.stringify()方法去转成json字符串。
    另外使用JQuery的ajax时,默认的content-type类型为application/x-www-form-urlcoded。
    一个JQuery的ajax代码例子
// Jquery Ajax请求
$.ajax({
    url:"doindex.action",
    type:"POST",
    contentType:"application/json;charset=UTF-8",
    data:JSON.stringify(json),
    dataType:"json",
    success:function(data){}});

注意,ajax中contentType : “application/json;charset=UTF-8”,这里面一定要写入charset=UTF-8类型,否则后端容易出现中文乱码,tomcat默认的字符集不是utf-8
在后端方法签名中使用@RequestBody注解,来读取前段传入的json字符串,看个例子

@RequestMapping("/doindex.action")public@ResponseBody CustomergetPages(@RequestBody(required=true) Map<String,Object> map, HttpServletRequest request)throws Exception{
        Customer cust=newCustomer();
        cust.setLcname("马雷");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));//mv.addObject(cust);//Map<String,Object> map=new HashMap();//map.put("cust",cust);
        ObjectMapper om=newObjectMapper();
        om.setDateFormat(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss"));//ModelAndView mv=new ModelAndView(new MappingJackson2JsonView(om),cust);return cust;}

不用管方法体中具体代码的意义,前端传来的json字符串参数信息自动就填充到@RequestBody(required=true) 标注的Map<String,Object> map类型中,这是如何做到的呢?
Spring 为我们内置了大量的HttpMessageConverter,例如, MappingJackson2HttpMessageConverter 、StringHttpMessageConverter 等。主要是为了配合@RequestBody和@ResponsetBody。
HttpMessageConverter接口是 Spring3.0 新添加的一个接 口,负责 1、将请求信息转换为一个对象(类型为 T),2、将对象( 类型为 T)输出为响应信息。
看一下接口代码

publicinterfaceHttpMessageConverter<T>{//指定转换器 可以读取的对象类型,即转换器是否可将请求信息转换为clazz类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)booleancanRead(Class<?> var1, MediaType var2);//指定转换器 是否可将 clazz类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。–LIst getSupportMediaTypes():该转换器支持的媒体类型booleancanWrite(Class<?> var1, MediaType var2);
	
    List<MediaType>getSupportedMediaTypes();//将请求信息流转换为 T 类型的对象
    Tread(Class<?extendsT> var1, HttpInputMessage var2)throws IOException, HttpMessageNotReadableException;//将T类型的对象写到响应流中,同时指定相应的媒体类型为contentTypevoidwrite(T var1, MediaType var2, HttpOutputMessage var3)throws IOException, HttpMessageNotWritableException;}

通过上面接口代码可以看出转换器中可以将请求信息进行转换,也可以将响应信息进行自动转换,所以基于前面的那个ajax请求的例子,后端代码接收方式可以是

//以下三种方法头都可以接收前段ajax传入的非application/x-www-form-urlcoded类型信息//将传入的json参数自动封装到map里,参数名成是key,参数值时valuepublic@ResponseBody CustomergetPages(@RequestBody(required=true) Map<String,Object> map, HttpServletRequest request)//将传入的json字符串直接复制给username字段public@ResponseBody CustomergetPages(@RequestBody(required=true) String username, HttpServletRequest request)//将传入的json字符串中每个字段的值赋给user对象实例对应名称的属性public@ResponseBody CustomergetPages(@RequestBody(required=true) User user, HttpServletRequest request)

注意:上面是使用SpringMVC默认转换器,如果自己在springmvc配置文件中指定了其他的转换器后,不保证上面自动封装能实现,具体看指定的转换器功能。

二、@ResponseBody注解

用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端.
注意:不一定只能传递字符串,若有对应的java类,springMVC会自动帮忙转换成符合要求的数据格式(json/xml)
下面就介绍几种实测后的可以实现返回给前段json相应类型的方法:

1、以下几种方法实现的前提配置

在web.xml中,在DispatcherServlet请求拦截中配置增加一个*.action的拦截,用来处理json返回的,如果不增加的话默认会调用已经配置的视图解析器去解析,解析失败就会报错406,所以添加一个新的映射类型后专门处理json返回。

<servlet-mapping><servlet-name>miniappservice</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 解决*.html后缀请求,无法返回json--><servlet-mapping><servlet-name>miniappservice</servlet-name><url-pattern>*.action</url-pattern></servlet-mapping>

当增加了请求拦截后

<servlet-mapping><servlet-name>miniappservice</servlet-name><url-pattern>*.action</url-pattern></servlet-mapping>

所有前段的需要返回json的请求,必须是以action为后缀的,当然action可以更改成任何名称比如do

2、几种返回json格式的@ResponseBody

由于在springmvc中处理json格式需要一些jar的包的支持,先在pom单中加入以下依赖:

<!--com.fasterxml.jackson.databind.ObjectMapper servlet配置需要--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.10.1</version></dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.10.1</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.1</version></dependency>
  • 使用MappingJackson2JsonView视图返回json
    springmvc的配置文件(**-servlet.xml)中配置jsp的视图解析器即可;
    springmvc的配置文件(**-servlet.xml)中配置<mvc:annotation-driven/>;
    方法使用@ResponseBody标注;
    在返回的ModelAndView中配置MappingJackson2JsonView视图,并将要返回json的实体实例、集合或map类型直接复制给ModelAndView,此处要注意以下,如果要返回的实体实例、集合或map类型加入ModelAndView时指定了名称则返回的json名称就是指定名称,如果没有指定名称则返回的json名称就是实体实例、集合或map类型名称的小写名称。看个例子
@RequestMapping("/test1.action")@ResponseBodypublic ModelAndViewgetDo1(@RequestBody(required=true) String username, HttpServletRequest request){
        Customer cust=newCustomer();
        cust.setLcname("测试");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));
        ModelAndView mv=newModelAndView(newMappingJackson2JsonView());
        mv.addObject(cust);return mv;}

前端请求过来的参数为json字符串{"username":"测试","password":"123456fg"},方法中标注@RequestBody字符串类型参数username获取的注入值为前端传过来的json字符串。
getDo1方法执行后返回给前段的响应输出为:

{"customer":{"sysId":null,"createtime":1577084421101,,"lcname":"马雷"}}

可以看出来,输出到前端的json是将cust实例的小写类名作为json字符串的key输出的,且日期createtime是按照时间戳类型输出的,如果要将时间输出是格式化,则需要使用com.fasterxml.jackson.databind.ObjectMapper类,此时将上面的代码修改一下如下:

@RequestMapping("/test2.action")@ResponseBodypublic ModelAndViewgetDo2(@RequestBody(required=true) String username, HttpServletRequest request){
        Customer cust=newCustomer();
        cust.setLcname("马雷");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));//引入格式化
        ObjectMapper om=newObjectMapper();
        om.setDateFormat(newSimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        ModelAndView mv=newModelAndView(newMappingJackson2JsonView(om));
        mv.addObject(cust);return mv;}

请求参数不变,相应结果如下:

{"customer":{"sysId":null,"2019-12-23 15:10:41","lcname":"马雷"}}
  • 直接返回对象类型输出json格式
    springmvc的配置文件(**-servlet.xml)中配置jsp的视图解析器即可;
    springmvc的配置文件(**-servlet.xml)中配置<mvc:annotation-driven/>;
    方法使用@ResponseBody标注;
    上方法:
@RequestMapping("/test3.action")public@ResponseBody Map<String,Object>getDo3(@RequestBody(required=true) String username, HttpServletRequest request){
        Customer cust=newCustomer();
        cust.setLcname("测试");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));
        Map<String,Object> map=newHashMap<>();
        map.put("customer",cust);return map;}

@ResponseBody可以标注在方法上也可以标注在方法签名上;
前端请求过来的参数依然为json字符串{"username":"测试","password":"123456fg"},下面的介绍中前端每次请求参数都是这个字符串,就不单独赘述了。
向前端相应输出结果为:

{"customer":{"sysId":null,"createtime":1577085315542,"lcname":"测试"}}

可以看出来,createtime依然是输出的时间戳,没有好的办法转化成日期时间格式。
注意:以上两中方法的区别
1、要格式化日期只能使用第一种;
2、如果输出的json需要{“key”:“value”,“key”:“value”}格式的可以使用第二种或者第一种、第二种使用map封装;

  • MappingJackson2HttpMessageConverter实现json输出
    首先在 springmvc的配置文件(**-servlet.xml)中配置下面代码
<!-- mvc:annotation-driven:使用mvc注解,解决406同时解决时间问题--><mvc:annotation-driven><mvc:message-converters><beanclass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><beanclass="com.fasterxml.jackson.databind.ObjectMapper"><property name="dateFormat"><beanclass="java.text.SimpleDateFormat"><constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"/></bean></property></bean></property></bean></mvc:message-converters></mvc:annotation-driven>

上面代码替换mvc:annotation-driven;
第二步在方法中或方法上使用@ResponseBody标注即可,看代码

@RequestMapping("/test4.action")public@ResponseBody CustomergetDo4(String username,String password, HttpServletRequest request){
        Customer cust=newCustomer();
        cust.setLcname("测试");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));return cust;}

前端使用application/x-www-form-urlcoded格式,url为test4.action?username=测试&password=123456fg;
响应输出为

{"sysId": null,"createtime":"2019-12-23 16:08:17","lcname":"测试"}

前端也可以使用application/json;charset=UTF-8格式,此时后端方法如下:

@RequestMapping("/test4.action")public@ResponseBody List<Customer>getDo4(@RequestBody(required=true) Map<String,Object> map1, HttpServletRequest request){
        Customer cust=newCustomer();
        cust.setLcname("测试");
        cust.setCreatetime(newTimestamp(System.currentTimeMillis()));
        List<Customer> map=newArrayList<>();
        map.add(cust);return map;}

这里要着重强调一下,当使用@RequestBody签名时,可以封装map接收前端传来的json字符串,也可以使用实体类接收前端传来的json字符串,不能直接使用string字符串去接收前端传来的json,使用就报400的错误,另外如果使用实体类接收前端串类的json字符串,实体类的属性少于传来的字符串的属性则也会报400错误。
用本种方法输出json的好处,可以将日期格式化;

另外介绍一个工具,posman用来模拟请求做调试特别的好,给出两个引用地址:
下载地址
使用说明
win7补丁

  • 作者:在路上奔跑~
  • 原文链接:https://blog.csdn.net/u011930054/article/details/103663772
    更新时间:2022-07-09 14:39:28