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<Account>
对象之前,所以我认为它指的是这个列表。课程文档指出,此注释的作用是:
确保结果将通过 HTTP 写入 HTTP 响应 消息转换器(而不是 MVC 视图)。
同时阅读官方 Spring 文档:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html
它似乎将List<Account>
对象放入Http Response
。这是正确的还是我误解了?
写入前面accountSummary()
方法的注释里面有:
访问时应该会得到 JSON 结果 http://localhost:8080/rest-ws/app/accounts
那么这到底是什么意思?是不是说accountSummary()
方法返回的List<Account>
对象自动转换成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 发送,但您需要将 produces
到 MediaType.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 Controller @ResponseBody text/xml 响应 UTF-8 编码问题