HttpMessageNotWritableException-没有带有预设内容类型的转换器

2023/05/11

1. 概述

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

首先,我们将阐明异常背后的主要原因。然后,我们将看看如何使用实际示例重现它,最后,如何解决它。

2. 原因

在深入细节之前,让我们试着理解异常的含义。

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

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

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

3. 重现异常

现在我们知道是什么原因导致Spring抛出异常,让我们看看如何使用实际示例重现它。

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

例如,让我们使用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"));
}

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

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

端点发回以下响应:

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

事实上,查看日志,Spring失败并出现HttpMessageNotWritableException异常:

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

因此,抛出异常是因为没有HttpMessageConverter能够将Student对象编组到XML和从XML中解组

最后,让我们创建一个测试用例来确认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 cn.tuyucheng.taketoday.boot.noconverterfound.model.Student] with preset Content-Type"));
}

4. 解决方案

只有一种方法可以修复异常-使用具有已注册消息转换器的媒体类型

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

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

话虽如此,并且知道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"));
}

现在,让我们创建一个测试用例来确认一切正常:

@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'}"));
}

正如我们所见,Spring不会抛出HttpMessageNotWritableException,这要归功于MappingJackson2HttpMessageConverter,它在后台处理Student对象到JSON的转换。

5. 总结

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

在此过程中,我们展示了如何产生异常以及如何在实践中修复它。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章