在 Spring MVC 中设置 JSON Content Type

1、简介

Content Type 表示请求/响应数据的媒体类型(Media Type)。当 Conroller 收到 Web 请求时,它会根据 Content Type 解析请求数据,然后根据 Content Type 响应数据。目前在 REST 中最流行的 Content Type 就是 JSON。

本文将会带你了解如何在 Spring MVC 中设置请求和响应的 Content Type。

2、@RequestMapping 注解

简而言之,@RequestMapping 是将 Web 请求映射到 Spring Controller 的重要注解。它有各种属性,包括 HTTP 方法、请求参数、Header 和媒体类型。

一般来说,媒体类型分为两类:消费(请求)、生产(响应)。也可以在 Spring 中定义自定义媒体类型。

其目的在于限制 Handler 可消费、生产的媒体类型。

2.1、请求的媒体类型

通过 consumes 属性指定 Controller 可接受的媒体类型,可以有多个。

定义一个简单的端点:

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

如果 Controller 不支持客户端指定的媒体类型,则会返回 HTTP “415 Unsupported Media Type” 错误。

2.2、响应的媒体类型

consumes 属性不同,produces 指定 Controller 发送回客户端的媒体类型,这也是一个列表,可以指定多个。如果系统不能按照这个媒体类型响应资源,则会响应 “406 Not Acceptable” 错误。

一个响应 JSON 字符串的示例 API 端点。

@GetMapping(
  value = "/greetings-with-response-body", 
  produces="application/json"
) 
public String getGreetingWhileReturnTypeIsString() { 
    return "{\"test\": \"Hello\"}";
}

使用 cURL 进行测试:

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

响应如下:

{ "test": "Hello" }

3、在 Spring Boot 中使用 Rest Controller

如果在 Spring Boot 中使用 Rest Controller,那么通过一个注解就可以处理很多事情。@RestController 注解将 @Controller@ResponseBody 注解合二为一,这会应用于该类中定义的所有端点。

3.1、使用 @RestController 注解

Jackson ObjectMapper 类可从字符串、Stream 或文件解析 JSON。如果 classpath 上有 Jackson,Spring 应用中的任何 Controller 都会默认渲染 JSON 响应。

添加一个单元测试,以验证响应:

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

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.2、使用 ResponseEntity

@ResponseBody 相反,ResponseEntity 是一种通用类型,代表整个 HTTP 响应。因此,可以控制其中的任何内容:状态码、Header 和 Body。

定义一个新的端点:

@GetMapping(
  value = "/greetings-with-response-entity",
  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);
}

在浏览器的控制台中,可以看到以下响应:

{"test": "Hello with ResponseEntity"}

通过测试用例来验证响应的 Content Type:

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

    Assert.assertEquals(expectedMimeType, actualMimeType);
}

3.3、返回 Map<String, Object>

还可以返回一个通用的 Map,这个 Map 会被解析为 JSON 字符串后响应给客户端。

定义一个新的端点:

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

测试:

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

响应如下:

{ "test": "Hello from map" }

4、总结

本文介绍了如何在 Spring boot 中为 Spring MVC 中设置请求和响应的 Content Type,以及如何通过 ResponseEntity 类控制响应的状态码、Header 和 Body。


Ref:https://www.baeldung.com/spring-mvc-set-json-content-type