HttpMessageNotWritableException: No Converter for [class …] With Preset Content-Type – HttpMessageNotWritableException No Converter for [class …] With Preset Content-Type

最后修改: 2022年 2月 10日

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

1. Overview

1.概述

In this short article, we’re going to take a close look at the Spring exception, “HttpMessageNotWritableException: no converter for [class …] with preset Content-Type”.

在这篇短文中,我们将仔细研究Spring的异常,“HttpMessageNotWritableException: no converter for [class …] with preset Content-Type”

First, we’ll shed light on the main cause behind the exception. Then, we’ll go down the rabbit hole to see how to reproduce it using a practical example and, finally, how to solve it.

首先,我们将阐明这个异常背后的主要原因。然后,我们将沿着兔子洞,用一个实际的例子看看如何重现它,最后,如何解决它。

2. The Cause

2.原因

Before diving deep into the details, let’s try to understand what the exception means.

在深入研究细节之前,让我们试着了解一下这个例外的含义。

The stack trace of the exception says it all: It tells us that Spring fails to find a suitable HttpMessageConverter capable of converting a Java object into the HTTP response.

异常的堆栈跟踪说明了一切:它告诉我们Spring 未能找到合适的HttpMessageConverter 有能力将Java对象转换为HTTP响应

Basically, Spring relies on the “Accept” header to detect the media type that it needs to respond with.

基本上,Spring依靠“Accept”头来检测它需要响应的媒体类型。

So, using a media type with no pre-registered message converter will cause Spring to fail with the exception.

因此,使用没有预先注册的消息转换器的媒体类型将导致Spring失败,出现 异常

3. Reproducing the Exception

3.复制例外情况

Now that we know what causes Spring to throw our exception, let’s see how to reproduce it using a practical example.

现在我们知道了导致Spring抛出异常的原因,让我们看看如何用一个实际的例子来重现它。

Let’s create a handler method and pretend to specify a media type (for the response) that has no registered HttpMessageConverter.

让我们创建一个处理方法并假装指定一个没有注册HttpMessageConverter的媒体类型(用于响应)。

For instance, let’s use APPLICATION_XML_VALUE or “application/xml”:

例如,让我们使用APPLICATION_XML_VALUE “application/xml”

@GetMapping(value = "/student/v3/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Student> getV3(@PathVariable("id") int id) {
    return ResponseEntity.ok(new Student(id, "Robert", "Miller", "BB"));
}

Next, let’s send a request to http://localhost:8080/api/student/v3/1 and see what happens:

接下来,让我们向http://localhost:8080/api/student/v3/1 发送一个请求,看看会发生什么。

curl http://localhost:8080/api/student/v3/1

The endpoint sends back this response:

端点会发回这个响应。

{"timestamp":"2022-02-01T18:23:37.490+00:00","status":500,"error":"Internal Server Error","path":"/api/student/v3/1"}

Indeed, looking at the logs, Spring fails with the HttpMessageNotWritableException exception:

事实上,查看日志,Spring以HttpMessageNotWritableException异常失败。

[org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.baeldung.boot.noconverterfound.model.Student] with preset Content-Type 'null']

So, the exception is thrown because there is no HttpMessageConverter capable of marshaling and unmarshaling Student objects to and from XML.

因此,该异常被抛出,因为没有HttpMessageConverter能够将Student对象转入和转出XML

Finally, let’s create a test case to confirm that Spring throws HttpMessageNotWritableException with the specified message:

最后,让我们创建一个测试案例,以确认Spring抛出HttpMessageNotWritableException ,并指定消息。

@Test
public void whenConverterNotFound_thenThrowException() throws Exception {
    String url = "/api/student/v3/1";

    this.mockMvc.perform(get(url))
      .andExpect(status().isInternalServerError())
      .andExpect(result -> assertThat(result.getResolvedException()).isInstanceOf(HttpMessageNotWritableException.class))
      .andExpect(result -> assertThat(result.getResolvedException()
        .getMessage()).contains("No converter for [class com.baeldung.boot.noconverterfound.model.Student] with preset Content-Type"));
}

4. The Solution

4.解决方案

There’s only one way to fix the exception – to use a media type that has a registered message converter.

只有一种方法可以解决这个异常–使用一个有注册信息转换器的媒体类型。

Spring Boot relies on auto-configuration to register the built-in message converters.

Spring Boot依靠自动配置来注册内置的消息转换器

For example, it will register MappingJackson2HttpMessageConverter automatically if the Jackson 2 dependency is present in the classpath.

例如,如果classpath中存在Jackson 2依赖项,它将自动注册MappingJackson2HttpMessageConverter

With that being said, and knowing that Spring Boot includes Jackson in the web starter, let’s create a new endpoint with the APPLICATION_JSON_VALUE media type:

既然如此,并且知道Spring Boot在web starter中包含Jackson,那么我们就用APPLICATION_JSON_VALUE媒体类型创建一个新的端点。

@GetMapping(value = "/student/v2/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Student> getV2(@PathVariable("id") int id) {
    return ResponseEntity.ok(new Student(id, "Kevin", "Cruyff", "AA"));
}

Now, let’s create a test case to confirm that everything works as excepted:

现在,让我们创建一个测试用例,以确认一切都按规定进行。

@Test
public void whenJsonConverterIsFound_thenReturnResponse() throws Exception {
    String url = "/api/student/v2/1";

    this.mockMvc.perform(get(url))
      .andExpect(status().isOk())
      .andExpect(content().json("{'id':1,'firstName':'Kevin','lastName':'Cruyff', 'grade':'AA'}"));
}

As we can see, Spring does not throw HttpMessageNotWritableException, thanks to MappingJackson2HttpMessageConverter, which handles the conversion of the Student object to JSON under the hood.

我们可以看到,Spring没有抛出HttpMessageNotWritableException,这要感谢MappingJackson2HttpMessageConverter,它处理了Student对象到JSON的转换。

5. Conclusion

5.总结

In this short tutorial, we discussed in detail what causes Spring to throw “HttpMessageNotWritableException No converter for [class …] with preset Content-Type”.

在这个简短的教程中,我们详细讨论了导致Spring抛出“HttpMessageNotWritableException No converter for [class …] with preset Content-Type”

Along the way, we showcased how to produce the exception and how to fix it in practice.

一路上,我们展示了如何产生异常以及如何在实践中修复它。

As always, the full source code of the examples is available over on GitHub.

一如既往,这些示例的完整源代码可在GitHub上获得over