Spring @ResponseBody 注释是如何工作的?

Posted

技术标签:

【中文标题】Spring @ResponseBody 注释是如何工作的?【英文标题】:How does the Spring @ResponseBody annotation work? 【发布时间】:2015-04-23 03:45:01 【问题描述】:

我有一个方法用以下方式注释:

/**
* Provide a list of all accounts.
*/
//  TODO 02: Complete this method.  Add annotations to respond
//  to GET /accounts and return a List<Account> to be converted.
//  Save your work and restart the server.  You should get JSON results when accessing 
//  http://localhost:8080/rest-ws/app/accounts
@RequestMapping(value="/orders", method=RequestMethod.GET)
public @ResponseBody List<Account> accountSummary() 
    return accountManager.getAllAccounts();

所以我通过这个注释知道:

@RequestMapping(value="/orders", method=RequestMethod.GET)

此方法处理对由 URL /orders 表示的资源发出的 GET HTTP 请求。

此方法调用返回 List 的 DAO 对象。

其中 Account 代表系统上的一个用户,并且有一些代表该用户的字段,例如:

public class Account 

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long entityId;

    @Column(name = "NUMBER")
    private String number;

    @Column(name = "NAME")
    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "ACCOUNT_ID")
    private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();

    ...............................
    ...............................
    ...............................

我的问题是:@ResponseBody 注释究竟是如何工作的?

它位于返回的List&lt;Account&gt; 对象之前,所以我认为它指的是这个列表。课程文档指出,此注释的作用是:

确保结果将通过 HTTP 写入 HTTP 响应 消息转换器(而不是 MVC 视图)。

同时阅读官方 Spring 文档:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

它似乎将List&lt;Account&gt; 对象放入Http Response。这是正确的还是我误解了?

写入前面accountSummary()方法的注释里面有:

访问时应该会得到 JSON 结果 http://localhost:8080/rest-ws/app/accounts

那么这到底是什么意思?是不是说accountSummary()方法返回的List&lt;Account&gt;对象自动转换成JSON格式再放入Http Response?还是什么?

如果这个断言为真,在哪里指定对象会自动转换成JSON格式?使用@ResponseBody注解时是采用标准格式还是在别处指定?

【问题讨论】:

【参考方案1】:

首先,注解没有注解List。它注释方法,就像RequestMapping 所做的那样。你的代码相当于

@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> accountSummary() 
    return accountManager.getAllAccounts();

现在注解的意思是方法的返回值将构成HTTP响应的主体。当然,HTTP 响应不能包含 Java 对象。因此,此帐户列表将转换为适合 REST 应用程序的格式,通常是 JSON 或 XML。

格式的选择取决于已安装的消息转换器、@RequestMapping 注释的produces attribute 的值以及客户端接受的内容类型(在 HTTP 请求标头中可用)。例如,如果请求说它接受 XML,但不接受 JSON,并且安装了可以将列表转换为 XML 的消息转换器,则将返回 XML。

【讨论】:

嗨,我们如何设置“已安装的消息转换器”?我希望默认值始终转换为 Json。我能做到吗? @JB Nizet 你能解释一下为什么http响应不能包含java对象吗?我是 java 新手。 @Er.NavedAli 阅读http规范,http响应内容类型定义了http响应可以包含的合法内容。其合法值可以是“application/octet-stream”、“image/jpeg”、“text/HTML”,但 java 对象不是它的合法值。 @ZhaoGang,在我的测试用例中,Content-type 'application/json' 已经被 'application/xml' 替换了,但是响应体似乎没有变化,仍然是 json 格式。 究竟在哪里,我的意思是在哪个spring类中,决定如何返回响应?你能指出我在 github 上的来源,这个决定是在哪里完成的或类名?另外,如果客户端接受 XML,但没有安装 XML 转换器,会发生什么?【参考方案2】:

首先要了解的是架构的差异。

你有 MVC 架构,它基于你的普通 web 应用程序,使用网页,浏览器请求页面:

Browser <---> Controller <---> Model
               |      |
               +-View-+

浏览器发出请求,控制器 (@Controller) 获取模型 (@Entity),并从模型创建视图 (JSP),并将视图返回给客户端。这是基本的网络应用架构。

另一方面,您有一个 RESTful 架构。在这种情况下,没有视图。控制器只发回模型(或资源表示,用更多的 RESTful 术语)。客户端可以是 javascript 应用程序、Java 服务器应用程序,以及我们向其中公开 REST API 的任何应用程序。使用此架构,客户决定如何处理此模型。以推特为例。 Twitter 作为 Web (REST) API,它允许我们的应用程序使用它的 API 来获取诸如状态更新之类的东西,以便我们可以使用它来将这些数据放入我们的应用程序中。该数据将以 JSON 等某种格式提供。

话虽如此,在使用 Spring MVC 时,它首先是为处理基本的 Web 应用程序架构而构建的。可能有不同的方法签名风格允许从我们的方法生成视图。该方法可以返回一个ModelAndView,我们在其中显式创建它,或者我们可以通过隐式方法返回一些设置为模型属性的任意对象。但无论哪种方式,在请求-响应周期的某个地方,都会产生一个视图。

但是当我们使用@ResponseBody 时,我们是在说我们不想生成视图。我们只想以我们指定的任何格式将返回对象作为主体发送。我们不希望它成为一个序列化的 Java 对象(尽管可能)。所以是的,它需要转换为其他一些常见的类型(这种类型通常通过内容协商处理 - 见下面的链接)。老实说,我对 Spring 的工作并不多,尽管我在这里和那里都涉猎过它。通常,我使用

@RequestMapping(..., produces = MediaType.APPLICATION_JSON_VALUE)

设置内容类型,但也许 JSON 是默认值。不要引用我的话,但是如果您正在获取 JSON,并且您没有指定 produces,那么它可能是默认值。 JSON 不是唯一的格式。例如,上面可以很容易地以 XML 发送,但您需要将 producesMediaType.APPLICATION_XML_VALUE,我相信您需要为 JAXB 配置 HttpMessageConverter。至于配置的 JSON MappingJacksonHttpMessageConverter,当我们在类路径中有 Jackson 时。

我需要一些时间来了解Content Negotiation。它是 REST 的一个非常重要的部分。它将帮助您了解不同的响应格式以及如何将它们映射到您的方法。

【讨论】:

如果在调查如何创建通用/动态 REST 控制器时找到了答案,而不使用 @Controller/@RestController。我发现,我需要以某种方式省略视图解析器层。这不是那么简单,因为 AbstractController 类提供了一个必须返回视图名称的方法。我对此提出了一个问题:***.com/questions/41016018/…,如果您对如何解决我的问题有一些想法,请发表评论。【参考方案3】:

除此之外,返回类型由

决定

    HTTP 请求所说的内容 - 在其 Accept 标头中。尝试查看初始请求,看看 Accept 设置为什么。

    什么 HttpMessageConverters Spring 设置。如果 Jackson 库在他的类路径上,Spring MVC 将为 XML(使用 JAXB)和 JSON 设置转换器。

如果有选择,它会选择一个 - 在本例中,它恰好是 JSON。

包含在课程笔记中。查找有关消息转换器和内容协商的注释。

【讨论】:

【参考方案4】:

@RequestBody 注释将 HTTPRequest 正文绑定到域对象。 Spring 使用 HttpMessageConverters 自动将传入的 HTTP 请求反序列化为对象。 HttpMessageConverter 根据请求的内容类型转换请求正文以解析方法参数。 很多例子如何使用转换器https://upcodein.com/search/jc/mg/ResponseBody/page/0

【讨论】:

以上是关于Spring @ResponseBody 注释是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

Spring配置@ResponseBody JSON格式

Spring Controller @ResponseBody text/xml 响应 UTF-8 编码问题

Spring Boot 注释

Spring @ExceptionHandler 不适用于 @ResponseBody

Spring @ResponseBody 返回中文乱码问题

Spring:responseBody:HTTP状态[重复]