知識庫 / Spring / Spring MVC RSS 訂閱

Spring框架下的HTTP消息轉換器

Spring MVC
HongKong
10
03:01 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將學習如何在 Spring 中配置 HttpMessageConverters

簡單來説,我們可以使用消息轉換器將 Java 對象轉換為和從 JSON 和 XML 通過 HTTP 進行轉換。

2. 基礎知識

2.1. 啓用 Web MVC

首先,Web 應用程序需要配置 Spring MVC 支持。 使用 @EnableWebMvc 註解,這是一種方便且高度可定製的方式:

@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
    
    // ...
    
}

請注意,該類實現了 WebMvcConfigurer,這將允許我們使用自定義的 Http 轉換器替換默認列表。

2.2. 默認消息轉換器

默認情況下,以下 <HttpMessageConverter> 實例已預先啓用:

  • ByteArrayHttpMessageConverter – 轉換字節數組
  • StringHttpMessageConverter – 轉換字符串
  • ResourceHttpMessageConverter – 轉換 org.springframework.core.io.Resource 用於任何類型的八進制流
  • SourceHttpMessageConverter – 轉換 javax.xml.transform.Source
  • FormHttpMessageConverter – 將表單數據轉換為/從 MultiValueMap<String, String>
  • Jaxb2RootElementHttpMessageConverter – 將 Java 對象轉換為/從 XML (僅當 JAXB2 在類路徑上存在時添加)
  • MappingJackson2HttpMessageConverter – 轉換 JSON (僅當 Jackson 2 在類路徑上存在時添加)
  • MappingJackson2HttpMessageConverter – 轉換 JSON (僅當 Jackson 在類路徑上存在時添加)
  • AtomFeedHttpMessageConverter – 轉換 Atom feeds (僅當 Rome 在類路徑上存在時添加)
  • RssChannelHttpMessageConverter – 轉換 RSS feeds (僅當 Rome 在類路徑上存在時添加)

3. 客户端-服務器通信 – JSON 僅限

3.1. 高級內容協商

每個 <em>HttpMessageConverter</em> 實現都有一個或多個關聯的 MIME 類型。

當接收到新的請求時,`Spring 將使用 “Accept” 頭來確定需要響應的媒體類型。

它會嘗試找到一個能夠處理該特定媒體類型的註冊轉換器。 最終,它將使用此轉換器來轉換實體並返回響應。

對於接收包含 JSON 信息的請求,該過程類似。 框架將 `使用 “Content-Type” 頭來確定請求體中的媒體類型。

然後它會搜索一個 <em style="line-height: 1.5em">HttpMessageConverter</em> 能夠將客户端發送的體轉換為 Java 對象。

讓我們通過一個快速示例來闡明這一點:

  • 客户端向 <em>/foos,</em> 發送一個 GET 請求,<em>Accept</em> 頭設置為 <em>application/json</em>,以獲取所有 <em>Foo</em> 資源作為 JSON。
  • <em>Foo</em> Spring 控制器被命中,並返回相應的 <em>Foo</em> Java 實體。
  • 然後 Spring 使用 Jackson 消息轉換器將實體轉換為 JSON。

現在讓我們看看這個過程的具體細節,以及如何利用 @ResponseBody 和 @RequestBody 註解。

3.2. @ResponseBody

@ResponseBody 在 Controller 方法上指示 Spring 將 方法的返回值直接序列化到 HTTP Response 的 body 中。 如前所述,“Accept” 頭部由客户端指定,將用於選擇用於將實體進行轉換的適當 Http Converter。

@GetMapping("/{id}")
public @ResponseBody Foo findById(@PathVariable long id) {
    return fooService.findById(id);
}

現在客户端將指定“Accept”頭為請求中的 application/json (例如,使用 curl 命令):

curl --header "Accept: application/json" 
  http://localhost:8080/spring-boot-rest/foos/1

Foo 級聯(Foo Pattern):

public class Foo {
    private long id;
    private String name;
}

以及 HTTP 響應體:

{
    "id": 1,
    "name": "Paul",
}
<div>
</div>

3.3. <em style="line-height: 1.5em">@RequestBody</em>

我們可以使用 <em style="line-height: 1.5em">@RequestBody</em> 註解,在 Controller 方法的參數上指示 HTTP 請求的主體內容將被反序列化為指定的 Java 實體。 Spring 將使用客户端請求的“Content-Type”頭信息來確定合適的轉換器。

@PutMapping("/{id}")
public @ResponseBody void update(@RequestBody Foo foo, @PathVariable String id) {
    fooService.update(foo);
}

接下來,我們將使用 JSON 對象來消費它,指定“Content-Type”為 application/json

curl -i -X PUT -H "Content-Type: application/json"  
-d '{"id":"83","name":"klik"}' http://localhost:8080/spring-boot-rest/foos/1

我們將會收到一個 200 OK 狀態碼,表示成功響應:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Fri, 10 Jan 2014 11:18:54 GMT

4. 自定義消息轉換器配置

我們還可以通過實現 WebMvcConfigurer 接口並覆蓋 configureMessageConverters 方法來自定義消息轉換器

@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        messageConverters.add(createXmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }

    private HttpMessageConverter<Object> createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xmlConverter.setMarshaller(xstreamMarshaller);
        xmlConverter.setUnmarshaller(xstreamMarshaller);

        return xmlConverter;
    } 
}

在此示例中,我們正在創建一個新的轉換器,即 MarshallingHttpMessageConverter,並使用 Spring XStream 支持進行配置。這提供了很大的靈活性,因為 我們正在使用底層 marshalling 框架的低級 API,在本例中是 XStream,並且我們可以以我們想要的方式進行配置。

請注意,此示例需要將 XStream 庫添加到類路徑中。

此外,通過擴展此支持類,我們正在失去之前預先註冊的默認消息轉換器。

當然,我們現在也可以通過定義自己的 MappingJackson2HttpMessageConverter 來實現同樣的效果。我們可以將自定義 ObjectMapper 設置在此轉換器上,並按照我們的需要進行配置。

在本例中,XStream 被選為 marshaller/unmarshaller 實現,但 其他,如 JibxMarshaller,也可以使用。

此時,在後端啓用了 XML 後,我們可以使用 XML 表示形式消費 API。

curl --header "Accept: application/xml" 
  http://localhost:8080/spring-boot-rest/foos/1

4.1. Spring Boot 支持

如果使用 Spring Boot,我們可以避免實現 WebMvcConfigurer 並手動添加所有 Message Converter,就像我們之前所做的那樣。

我們可以只需在上下文中定義不同的 HttpMessageConverter 實例,Spring Boot 將會自動將其添加到它創建的自動配置中:

@Bean
public HttpMessageConverter<Object> createXmlHttpMessageConverter() {
    MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

    // ...

    return xmlConverter;
}

5. 使用 Spring 的 RestTemplate 結合 HTTP 消息轉換器

除了在服務端,HTTP 消息轉換也可以配置在 Spring 的 RestTemplate 的客户端端。

我們將配置模板,在適當的情況下設置 “Accept” 和 “Content-Type” 請求頭。然後,我們將嘗試使用完整的映射和反向映射來消費 REST API,具體包括 Foo 資源,以 JSON 和 XML 兩種格式。

5.1. 不帶 Accept 標頭檢索資源

@Test
public void whenRetrievingAFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    Foo resource = restTemplate.getForObject(URI, Foo.class, "1");

    assertThat(resource, notNullValue());
}

5.2. 使用 application/xml Accept 標頭檢索資源

現在,我們明確地以 XML 表示形式檢索資源。我們將定義一組轉換器並將它們設置為 RestTemplate

由於我們正在消費 XML,因此我們將使用之前使用的相同的 XStream 序列化器:

@Test
public void givenConsumingXml_whenReadingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getXmlMessageConverters());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML));
    HttpEntity<String> entity = new HttpEntity<>(headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
    Foo resource = response.getBody();

    assertThat(resource, notNullValue());
}

private List<HttpMessageConverter<?>> getXmlMessageConverters() {
    XStreamMarshaller marshaller = new XStreamMarshaller();
    marshaller.setAnnotatedClasses(Foo.class);
    MarshallingHttpMessageConverter marshallingConverter = 
      new MarshallingHttpMessageConverter(marshaller);

    List<HttpMessageConverter<?>> converters = new ArrayList<>();
    converters.add(marshallingConverter);
    return converters;
}

5.3. 使用 application/json Accept 標頭檢索資源

同樣,現在我們通過請求 JSON 來消費 REST API:

@Test
public void givenConsumingJson_whenReadingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos/{id}";

    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getJsonMessageConverters());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    HttpEntity<String> entity = new HttpEntity<String>(headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
    Foo resource = response.getBody();

    assertThat(resource, notNullValue());
}

private List<HttpMessageConverter<?>> getJsonMessageConverters() {
    List<HttpMessageConverter<?>> converters = new ArrayList<>();
    converters.add(new MappingJackson2HttpMessageConverter());
    return converters;
}

5.4. 使用 XML 內容類型更新資源

最後,我們將向 REST API 發送 JSON 數據,並通過 Content-Type 標頭指定該數據的媒體類型:

@Test
public void givenConsumingXml_whenWritingTheFoo_thenCorrect() {
    String URI = BASE_URI + "foos";
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(getJsonAndXmlMessageConverters());

    Foo resource = new Foo("jason");
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.setContentType((MediaType.APPLICATION_XML));
    HttpEntity<Foo> entity = new HttpEntity<>(resource, headers);

    ResponseEntity<Foo> response = 
      restTemplate.exchange(URI, HttpMethod.POST, entity, Foo.class);
    Foo fooResponse = response.getBody();

    assertThat(fooResponse, notNullValue());
    assertEquals(resource.getName(), fooResponse.getName());
}

private List<HttpMessageConverter<?>> getJsonAndXmlMessageConverters() {
    List<HttpMessageConverter<?>> converters = getJsonMessageConverters();
    converters.addAll(getXmlMessageConverters());
    return converters;
}

有趣的地方在於我們能夠混合不同的媒體類型。我們正在發送 XML 數據,但正在等待服務器返回 JSON 數據。 這充分展示了 Spring 轉換機制的強大之處。

6. 結論

在本文中,我們學習了 Spring MVC 如何允許我們指定並完全自定義 Http Message Converter,以自動將 Java 實體映射到和從 XML 或 JSON。這當然只是一個簡化的定義,正如最後一個測試示例所表明的,消息轉換機制可以做的事情遠不止於此。

我們還探討了如何利用相同的強大機制與 RestTemplate 客户端,從而實現完全類型安全的 API 消費。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.