`

解析Spring中的ResponseBody和RequestBody

 
阅读更多
https://www.cnkirito.moe/2017/08/30/%E8%A7%A3%E6%9E%90Spring%E4%B8%AD%E7%9A%84ResponseBody%E5%92%8CRequestBody/

spring,restful,前后端分离这些关键词都是大家耳熟能详的关键词了,一般spring常常需要与前端、第三方使用JSON,XML等形式进行交互,你也一定不会对@RequestBody和@ResponseBody这两个注解感到陌生。

@ResponseBody的使用

由于@ResponseBody和@RequestBody的内部实现是同样的原理(封装请求和封装响应),所以本文以@ResponseBody为主要入手点,理解清楚任何一者,都可以同时掌握另一者。

如果想要从spring获得一个json形式返回值,操作起来是非常容易的。首先定义一个实体类:


public class Book {
    private Integer id;
    private String bookName;
}
接着定义一个后端端点:


@RestController
public class BookController {
    @GetMapping(value = "/book/{bookId}")
    public Book getBook(@PathVariable("bookId") Integer bookId) {
        return new Book(bookId, "book" + bookId);
    }
}
在RestController中,相当于给所有的xxxMapping端点都添加了@ResponseBody注解,不返回视图,只返回数据。使用http工具访问这个后端端点localhost:8080/book/2,便可以得到如下的响应:

1
2
3
4
{
    "id": 2,
    "bookName": "book2"
}
这是一个最简单的返回JSON对象的使用示例了,相信这样的代码很多人在项目中都写过。

添加XML解析

如果我们需要将Book对象以XML的形式返回,该如何操作呢?这也很简单,给Book对象添加@XmlRootElement注解,让spring内部能够解析XML对象。

@XmlRootElement
public class Book {
    private Integer id;
    private String bookName;
}
在我们未对web层的BookController做任何改动之前,尝试访问localhost:8080/book/2时,会发现得到的结果仍然是前面的JSON对象。这也能够理解,因为Book对象如今既可以被解析为XML,也可以被解析为JSON,我们隐隐察觉这背后有一定的解析顺序关系,但不着急,先看看如何让RestController返回XML解析结果。

方法1 http客户端指定接收的返回结果类型

http协议中,可以给请求头添加Accept属性,笔者常用的http客户端是idea自带的Test RESTful Web Service以及chrome的插件Postman。简单的调试,前者基本可以满足我们大多数的需求,而这里为了给大家更直观的体验,笔者使用了Postman。以code形式展示:

1
2
3
GET /book/2 HTTP/1.1
Host: localhost:8080
Accept: application/xml
响应内容如下:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book>
    <bookName>book2</bookName>
    <id>2</id>
</book>
方法2 在RestController后端端点中指定返回类型

修改后的RestController如下所示


@RestController
public class BookController {
    @GetMapping(value = "/book/{bookId}", produces = {"application/xml"})
    public Book getBook(@PathVariable("bookId") Integer bookId) {
        return new Book(bookId, "book" + bookId);
    }
}
此时即使将请求中的Accept: application/xml去除,依旧可以返回上述的XML结果。

通常情况下,我们的服务端返回的形式一般是固定的,即限定了是JSON,XML中的一种,不建议依赖于客户端添加Accept的信息,而是在服务端限定produces类型。

详解Accpect与produces

Accpect包含在http协议的请求头中,其本身代表着客户端发起请求时,期望返回的响应结果的媒体类型。如果服务端可能返回多个媒体类型,则可以通过Accpect指定具体的类型。

produces是Spring为我们提供的注解参数,代表着服务端能够支持返回的媒体类型,我们注意到produces后跟随的是一个数组类型,也就意味着服务端支持多种媒体类型的响应。

在上一节中,我们未显示指定produces值时,其实就隐式的表明,支持XML形式,JSON形式的媒体类型响应。从实验结果,我们也可以看出,当请求未指定Accpect,响应未指定produces时,具体采用何种形式返回是有Spring控制的。在接口交互时,最良好的对接方式,当然是客户端指定Accpect,服务端指定produces,这样可以避免模棱两可的请求响应,避免出现意想不到的对接结果。

详解ContentType与consumes

恰恰和Accpect&produces相反,这两个参数是与用于限制请求的。理解了前两者的含义,这两个参数可以举一反三理解清楚。

ContentType包含在http协议的请求头中,其本身代表着客户端发起请求时,告知服务端自己的请求媒体类型是什么。

consumes是Spring为我们提供的注解参数,代表着服务端能够支持处理的请求媒体类型,同样是一个数组,意味着服务端支持多种媒体类型的请求。一般而言,consumes与produces对请求响应媒体类型起到的限制作用,我们给他一个专有名词:窄化。

http请求响应媒体类型一览

上面描述的4个属性:Accpect与produces,ContentType与consumes究竟有哪些类型与之对应呢?我只将常用的一些列举了出来:

媒体类型 含义
text/html HTML格式
text/plain 纯文本格式
text/xml, application/xml XML数据格式
application/json JSON数据格式
image/gif gif图片格式
image/png png图片格式
application/octet-stream 二进制流数据
application/ x-www-form-urlencoded form表单数据
multipart/form-data 含文件的form表单
其中有几个类型值得一说,web开发中我们常用的提交表单操作,其默认的媒体类型就是application/ x-www-form-urlencoded,而当表单中包含文件时,大家估计都踩过坑,需要将enctype=multipart/form-data设置在form参数中。text/html也就是常见的网页了,json与xml常用于数据交互,其他不再赘述。

而在JAVA中,提供了MediaType这样的抽象,来与http的媒体类型进行对应。‘/’之前的名词,如text,application被称为类型(type),‘/’之后被称为子类型(subType)。

详解HttpMessageConverter

我们想要搞懂Spring到底如何完成众多实体类等复杂类型的数据转换以及与媒体类型的对应,就必须要搞懂HttpMessageConverter这个顶级接口:


public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, MediaType var2);
    boolean canWrite(Class<?> var1, MediaType var2);
    List<MediaType> getSupportedMediaTypes();
    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
    void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
大致能看出Spring的处理思路。下面的流程图可以更好方便我们的理解:

HttpMessageConverter运转流程
HttpMessageConverter运转流程

对于添加了@RequestBody和@ResponseBody注解的后端端点,都会经历由HttpMessageConverter进行的数据转换的过程。而在Spring启动之初,就已经有一些默认的转换器被注册了。通过在RequestResponseBodyMethodProcessor 中打断点,我们可以获取到一个converters列表:

内置转换器列表
内置转换器列表

源码方面不做过多的解读,有兴趣的朋友可以研究一下RequestResponseBodyMethodProcessor 中的handleReturnValue方法,包含了转换的核心实现。

自定义HttpMessageConverter

前面已经提及了消息转换器是通过判断媒体类型来调用响应的转换类的,不禁引发了我们的思考,如果我们遇到了不常用的MediaType,或者自定义的MediaType,又想要使用Spring的@RequestBody,@ResponseBody注解,该如何添加代码呢?下面我们通过自定义一个HttpMessageConverter来了解Spring内部的转换过程。

先定义我们的需求,自定一个MediaType:application/toString,当返回一个带有@ResponseBody注解的实体类时,将该实体类的ToString作为响应内容。

1 首先重写Book的ToString方法,方便后期效果展示


@Override
public String toString() {
    return "~~~Book{" +
            "id=" + id +
            ", bookName='" + bookName + '\'' +
            "}~~~";
}
2 编写自定义的消息转换器


public class ToStringHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
    public ToStringHttpMessageConverter() {
        super(new MediaType("application", "toString", Charset.forName("UTF-8")));// <1>
    }
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }
    //从请求体封装数据 对应RequestBody 用String接收
    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return StreamUtils.copyToString(inputMessage.getBody(), Charset.forName("UTF-8"));
    }
    //从响应体封装数据 对应ResponseBody
    @Override
    protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        String result = o.toString();//<2>
        outputMessage.getBody().write(result.getBytes());
    }
}
<1> 此处指定了支持的媒体类型
<2> 调用类的ToString方法,将结果写入到输出流中
3 配置自定义的消息转换器


@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new ToStringHttpMessageConverter());
    }
}
4 配置后端端点,指定生产类型


@RestController
public class BookController {
    @GetMapping(value = "/book/{bookId}",produces = {"application/toString","application/json","application/xml"})
    public Book getBook(@PathVariable("bookId") Integer bookId) {
        return new Book(bookId, "book" + bookId);
    }
}
此处只是为了演示,添加了三个生产类型,我们的后端端点可以支持输出三种类型,而具体输出哪一者,则依赖客户端的Accept指定。

5 客户端请求

1
2
3
GET /book/2 HTTP/1.1
Host: localhost:8080
Accept: application/toString
响应结果如下:

1
​~~~Book{id=2, bookName='book2'}~~~
此时,你可以任意指定Accept的类型,即可获得不同形式的Book返回结果,可以是application/toString,application/json,application/xml,都会对应各自的HttpMessageConverter
分享到:
评论

相关推荐

    springMvc注解之 ResponseBody和 RequestBody.docx

    在Spring MVC中,`@ResponseBody`和`@RequestBody`是两个关键的注解,它们用于处理HTTP请求和响应中的数据转换。这两个注解是Spring框架对RESTful API开发的重要支持,使得开发者能够方便地处理JSON格式的数据。 ...

    Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariable

    总之,`@ResponseBody`、`@RequestBody`和`@PathVariable`是Spring MVC中不可或缺的三大注解,它们在构建RESTful服务时起着至关重要的作用。掌握它们的用法和工作原理,能够提升我们的开发效率,使我们更好地利用...

    彻底根治Spring @ResponseBody JavaScript HTML特殊字符

    在开发Web应用时,我们经常会遇到一个问题:当使用Spring MVC的`@ResponseBody`注解将后端处理结果直接转化为HTTP响应体时,如果这个结果中包含HTML特殊字符,如尖角号、引号、按位与符号等,浏览器可能会误解析,...

    SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换(上)

    在实际开发中,`@RequestBody` 和 `@ResponseBody` 的组合使用使得Spring MVC能够轻松地处理RESTful API的请求和响应。它们简化了数据交换的过程,减少了手动序列化和反序列化的代码,提高了开发效率。通过理解这两...

    @ResponseBody 和 @RequestBody 注解的区别

    在 Spring 框架中,@ResponseBody 和 @RequestBody 是两个常用的注解,它们都用于处理 HTTP 请求和响应,但是它们的作用和使用场景却有所不同。 一、@ResponseBody 注解 @ResponseBody 注解表示该方法的返回结果...

    浅析@ReponseBody和@RequestBody注解

    @ReponseBody和@RequestBody是Spring MVC框架中两个重要的注解,分别用于处理HTTP请求和响应体。下面我们将详细分析这两个注解的作用和Spring MVC内部是如何对他们进行解析的。 @RequestBody注解 @RequestBody注解...

    SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换(下)

    在Spring MVC框架中,`@RequestBody` 和 `@ResponseBody` 是两个非常重要的注解,它们在处理HTTP请求和响应时起到了关键作用。本篇文章将详细解释这两个注解的工作原理、使用场景以及如何实现Java对象与XML/JSON数据...

    springMVC的 RequestBody和 ResponseBody和RequestParam.docx

    ### Spring MVC 中的 RequestBody 和 ResponseBody 及 RequestParam 使用详解 #### 一、Spring MVC 概述 Spring MVC 是 Spring Framework 的一部分,它提供了一种清晰的模型—视图—控制器架构实现,用于构建 Web ...

    Spring MVC打印@RequestBody、@Response日志的方法

    Spring MVC框架提供了强大的日志记录功能,对于日志记录的实现,Spring MVC提供了多种方式,本文将主要介绍如何使用RequestBodyAdvisor和ResponseBodyAdvisor来实现对@RequestBody和@Response的日志输出。...

    springMvc注解之@ResponseBody和@RequestBody详解

    Spring MVC注解之@ResponseBody和@RequestBody详解 在Spring MVC框架中,@ResponseBody和@RequestBody是两个非常重要的注解,它们分别用于处理HTTP请求响应体和请求体的序列化和反序列化。下面,我们将详细介绍这两...

    springmvc实现json交互-requestBody和responseBody

    在Spring MVC中,`@RequestBody`和`@ResponseBody`两个注解是实现JSON交互的关键。`@RequestBody`注解用于读取HTTP请求的内容,通常是请求体中的JSON字符串。Spring MVC通过HttpMessageConverter接口将这个字符串...

    最新版本 jackson 2.8.2 jar与spring 4.x @ResponseBody配合使用

    3. **Spring MVC与@RequestBody和@ResponseBody**: - **@RequestBody**:此注解用于控制器方法的参数,表示请求体中的内容应被转换为该参数的对象。Spring会使用合适的HttpMessageConverter(如Jackson的`...

    spring3-miniweb.rar_spring json_spring3_spring3 mvc

    2. **@RequestBody 和 @ResponseBody**:这两个Spring MVC注解是处理JSON的核心。`@RequestBody`用于将HTTP请求体中的JSON数据映射到Java对象,而`@ResponseBody`则将控制器方法返回的对象转换为JSON响应。 3. **...

    7-Spring-mvc-ajax支持1

    本文将深入探讨Spring MVC如何支持Ajax以及相关的注解,如`@RequestBody`和`@ResponseBody`。 首先,`&lt;mvc:annotation-driven/&gt;`在Spring配置文件中是一个重要的元素,它告诉Spring启用注解驱动,自动注册一些关键...

    SpringMVC restful 注解之@RequestBody进行json与object转换

    在Spring MVC中,`@RequestBody`注解是用于将HTTP请求体中的数据转换为Java对象的,特别适用于处理JSON或XML格式的数据。当控制器方法接收POST、PUT等带有请求体的HTTP请求时,`@RequestBody`可以帮助我们将接收到的...

    spring mvc(整合了json)

    4. **控制器方法的编写**:在 Spring MVC 的控制器类中,我们可以定义处理 HTTP 请求的方法,并使用 `@RequestBody` 和 `@ResponseBody` 注解来接收和返回 JSON 数据。例如: ```java @RequestMapping(value = "/...

    spring中开发webservice

    JSON和XML数据可以通过`@RequestBody`和`@ResponseBody`进行处理。 9. **测试**: 测试Web服务是必不可少的。Spring提供了`WebServiceMessageReceiverObjectSupport`和`WebServiceTemplate`,帮助进行单元测试和...

    Spring MVC 项目+ 源码解析

    你可以使用@RequestMapping注解来映射HTTP请求,并使用@RequestBody和@ResponseBody注解处理请求体和响应体。 3. **ModelAndView对象**:在处理完请求后,Controller可以返回一个ModelAndView对象,其中包含模型...

    spring 3.x 中Restful风格服务开发 demo

    在`web.xml`中配置DispatcherServlet,并在`applicationContext.xml`中配置视图解析器和`HandlerMapping`。如果你使用的是Spring Boot,这些配置将更为简化。 此外,`springrestful`这个压缩包可能包含了一个运行该...

    @Response和@Request

    当控制器方法的参数带有`@RequestBody`时,Spring会查找合适的`HttpMessageConverter`来解析请求体的数据,将其转换成方法参数所期望的对象类型。`HttpMessageConverter`是Spring MVC中用于处理不同格式数据(如JSON...

Global site tag (gtag.js) - Google Analytics