How to Set JSON Content Type In Spring MVC – 如何在Spring MVC中设置JSON内容类型

最后修改: 2021年 8月 3日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The content type indicates how to interpret the data present in the request/response. Whenever a controller receives a web request, it consumes or produces some media types. In this request-response model, several media types can be consumed/produced, and JSON is one of them.

内容类型表明如何解释请求/响应中存在的数据。每当一个控制器收到一个网络请求,它就会消耗或产生一些媒体类型。在这个请求-响应模型中,有几种媒体类型可以被消费/生产,JSON就是其中之一。

In this quick tutorial, we’ll explore different ways to set the content type in Spring MVC.

在这个快速教程中,我们将探讨在Spring MVC中设置内容类型的不同方法。

2. @RequestMapping in Spring

2.@RequestMapping in Spring

Simply put, @RequestMapping is an important annotation that maps web requests to a Spring controller. It has various attributes, including HTTP method, request parameters, headers, and media types.

简单地说,@RequestMapping是一个重要的注解,它将Web请求映射到Spring控制器上。它有各种属性,包括HTTP方法、请求参数、头文件和媒体类型。

Generally, media types fall into two categories: consumable and producible. In addition to these, we can also define a custom media type in Spring. The main purpose is to restrict the primary mapping to a list of media types for our request handler.

一般来说,媒体类型分为两类:可消费和可生产。除此以外,我们还可以在Spring中定义一个自定义的媒体类型其主要目的是为我们的请求处理程序将主要映射限制在媒体类型的列表中。

2.1. Consumable Media Type

消耗性介质类型

With the consumes attribute, we can specify the media type that the controller will accept from a client. We can provide a list of media types too. Let’s define a simple endpoint:

通过consumes 属性,我们可以指定控制器将从客户端接受的媒体类型。我们也可以提供一个媒体类型的列表。让我们定义一个简单的端点。

@RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json")
public void addGreeting(@RequestBody ContentType type, Model model) {
    // code here
}

If a client specifies a media type that’s unable to consume by resource, the system will generate an HTTP “415 Unsupported Media Type” error.

如果客户端指定的媒体类型无法被资源消费,系统将产生一个HTTP “415不支持的媒体类型 “错误。

2.2. Producible Media Type

2.2. 可生产的媒体类型

As opposed to the consumes attribute, produces specifies the media type a resource can produce and send back to the client. Without a doubt, we can use a list of options. If a resource is unable to produce the requested resource, the system will generate an HTTP “406 Not Acceptable” error.

consumes属性相反,produces指定了一个资源可以生产并送回客户端的媒体类型。毫无疑问,我们可以使用一个选项列表。如果一个资源无法产生所请求的资源,系统将产生一个HTTP “406不可接受 “的错误。

Let’s start with a simple example of an API exposing a JSON string.

让我们从一个揭露JSON字符串的API的简单例子开始。

Here’s our endpoint:

这是我们的端点。

@RequestMapping(
  value = "/greetings-with-response-body", 
  method = RequestMethod.GET, 
  produces="application/json"
) 
@ResponseBody
public String getGreetingWhileReturnTypeIsString() { 
    return "{\"test\": \"Hello using @ResponseBody\"}";
}

We’ll test this using CURL:

我们将使用CURL进行测试。

curl http://localhost:8080/greetings-with-response-body

The above command produces the response:

上述命令产生的响应。

{ "test": "Hello using @ResponseBody" }

Based on the content type present in the header, @ResponseBody only binds a method return value to the web response body.

基于头中存在的内容类型,@ResponseBody 只将一个方法的返回值绑定到网络响应体。

3. Content-Type Not Being Set Properly

3.内容类型设置不正确

When a method has a return type String, and no JSON Mapper present at the classpath, the return value is handled by the StringHttpMessageConverter class, which sets the content type to “text/plain.” This often leads to an issue where the controller is unable to produce the expected content type.

当一个方法的返回类型为String,且classpath处没有JSON Mapper时,返回值由StringHttpMessageConverter类处理,它将内容类型设置为 “text/plain”。这通常会导致一个问题,即控制器无法产生预期的内容类型。

Let’s look at different approaches to solving this issue.

让我们来看看解决这个问题的不同方法。

3.1. Using @ResponseBody With JSON Mapper

3.1.使用@ResponseBody与JSON Mapper

The Jackson ObjectMapper class parses a JSON from a string, stream, or file. If Jackson is on the classpath, any controller in Spring applications renders the JSON response by default.

Jackson ObjectMapper类解析了来自字符串、流或文件的JSON。如果Jackson在classpath上,Spring应用程序中的任何控制器都会默认渲染JSON响应。

To include Jackson on the classpath, we need to add the following dependency in pom.xml:

为了在classpath上包含Jackson,我们需要在pom.xml中添加以下依赖项。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

We’ll add a unit test to verify from the response:

我们将添加一个单元测试,从响应中验证。

@Test
public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON() 
  throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.2. Using ResponseEntity

3.2.使用ResponseEntity

In contrast to @ResponseBody, the ResponseEntity is a generic type that represents the entire HTTP response. As a result, we can control anything that goes into it: status code, header, and the body.

@ResponseBody相比,ResponseEntity是一个通用类型,代表整个HTTP响应。因此,我们可以控制进入它的任何东西:状态码、头和正文。

Let’s define a new endpoint:

我们来定义一个新的端点。

@RequestMapping(
  value = "/greetings-with-response-entity",
  method = RequestMethod.GET, 
  produces = "application/json"
)
public ResponseEntity<String> getGreetingWithResponseEntity() {
    final HttpHeaders httpHeaders= new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<String>("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK);
}

In the developer console of our browser, we can see the following response:

在我们浏览器的开发者控制台,我们可以看到以下响应。

{"test": "Hello with ResponseEntity"}

With ResponseEntity, we should have the annotation-driven tag in our dispatcher servlet:

有了ResponseEntity,我们应该在dispatcher servlet中设置注释驱动的标签。

<mvc:annotation-driven />

Simply put, the above tag gives greater control over the inner workings of Spring MVC.

简单地说,上述标签对Spring MVC的内部运作提供了更大的控制。

We’ll verify the response’s content type with a test case:

我们将用一个测试案例来验证响应的内容类型。

@Test
public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception {
    
    // Given
    String expectedMimeType = "application/json";
    
    // Then
    String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1))
      .andReturn().getResponse().getContentType();

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.3. Using Map<String, Object> Return Type

3.3.使用Map<String, Object>返回类型

Last, but not least, we can also set the content type by changing the return type from String to Map. This Map return type will need marshaling, and returns a JSON object.

最后,但不是最不重要的,我们还可以通过将返回类型从String改为Map来设置内容类型。这个Map返回类型将需要进行marshaling,并返回一个JSON对象。

Here’s our new endpoint:

这是我们的新端点。

@RequestMapping(
  value = "/greetings-with-map-return-type", 
  method = RequestMethod.GET, 
  produces = "application/json"
)
@ResponseBody
public Map<String, Object> getGreetingWhileReturnTypeIsMap() {
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("test", "Hello from map");
    return map;
}

Let’s see this in action:

让我们看看这个行动。

curl http://localhost:8080/greetings-with-map-return-type

The curl command returns a JSON response:

curl命令返回一个JSON响应。

{ "test": "Hello from map" }

4. Conclusion

4.总结

In this article, we learned how to set the content type in Spring MVC, first by adding Json mapper in the classpath, then using ResponseEntity, and finally, changing the return type from String to Map.

在这篇文章中,我们学习了如何在Spring MVC中设置内容类型,首先在classpath中添加Json mapper,然后使用ResponseEntity,最后,将返回类型从String改为Map。

As always, all the code snippets can be found over on GitHub.

一如既往,所有的代码片段都可以在GitHub上找到