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

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 Customer getPages(@RequestBody(required=true) Map<String,Object> map , HttpServletRequest request)throws Exception{
    
    
        Customer cust=new Customer();
        cust.setLcname("马雷");
        cust.setCreatetime(new Timestamp(System.currentTimeMillis()));
        //mv.addObject(cust);
        //Map<String,Object> map=new HashMap();
        //map.put("cust",cust);
        ObjectMapper om=new ObjectMapper();
        om.setDateFormat(new SimpleDateFormat("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)输出为响应信息。
看一下接口代码

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

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

  //以下三种方法头都可以接收前段ajax传入的非application/x-www-form-urlcoded类型信息
 //将传入的json参数自动封装到map里,参数名成是key,参数值时value
 public @ResponseBody Customer getPages(@RequestBody(required=true) Map<String,Object> map , HttpServletRequest request)
 //将传入的json字符串直接复制给username字段
 public @ResponseBody Customer getPages(@RequestBody(required=true) String username, HttpServletRequest request)
 //将传入的json字符串中每个字段的值赋给user对象实例对应名称的属性
 public @ResponseBody Customer getPages(@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")
    @ResponseBody
    public ModelAndView getDo1(@RequestBody(required=true) String username , HttpServletRequest request){
    
    
        Customer cust=new Customer();
        cust.setLcname("测试");
        cust.setCreatetime(new Timestamp(System.currentTimeMillis()));
        ModelAndView mv=new ModelAndView(new MappingJackson2JsonView());
        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")
    @ResponseBody
    public ModelAndView getDo2(@RequestBody(required=true) String username , HttpServletRequest request){
    
    
        Customer cust=new Customer();
        cust.setLcname("马雷");
        cust.setCreatetime(new Timestamp(System.currentTimeMillis()));
        //引入格式化
        ObjectMapper om=new ObjectMapper();
        om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        ModelAndView mv=new ModelAndView(new MappingJackson2JsonView(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=new Customer();
        cust.setLcname("测试");
        cust.setCreatetime(new Timestamp(System.currentTimeMillis()));
        Map<String,Object> map=new HashMap<>();
        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>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                        <property name="dateFormat">
                            <bean class="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 Customer getDo4(String username,String password, HttpServletRequest request){
    
    
        Customer cust=new Customer();
        cust.setLcname("测试");
        cust.setCreatetime(new Timestamp(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=new Customer();
        cust.setLcname("测试");
        cust.setCreatetime(new Timestamp(System.currentTimeMillis()));
        List<Customer> map=new ArrayList<>();
        map.add(cust);
        return map;
    }

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

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

猜你喜欢

转载自blog.csdn.net/u011930054/article/details/103663772