HttpMessageNotWritableException: "No converter found for return value of type"

1、概览

本文将带你了解 Spring 中出现 HttpMessageNotWritableException: "No converter found for return value of type" 异常的原因以及解决办法。

2、原因

通常,当 Spring 无法获取返回对象的属性时,就会出现这种异常。

导致这种异常的最典型原因通常是返回对象的属性没有任何 public getter 方法。

默认情况下,Spring Boot 依赖于 Jackson 库来完成序列化/反序列化请求和响应对象的所有工作。

因此,导致异常的另一个常见原因可能是 缺少或使用了错误的 Jackson 依赖

简而言之,这种异常情况的一般准则是检查是否存在以下情况:

  • 默认构造器
  • Getter 方法
  • Jackson 依赖

注意,异常类型 已从 java.lang.IllegalArgumentException 变为 org.springframework.http.converter.HttpMessageNotWritableException

3、实例

现在,来看看一个会产生 org.springframework.http.converter.HttpMessageNotWritableException: "No converter found for return value of type" 异常的示例

使用 Spring Boot 构建一个基本的 REST API。

首先,创建 Student Model 类,并假装忘记生成 Getter 方法:

public class Student {

    private int id;
    private String firstName;
    private String lastName;
    private String grade;

    public Student() {
    }

    public Student(int id, String firstName, String lastName, String grade) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.grade = grade;
    }

    // Setter
}

其次,创建一个 Spring Controller,其中包含一个 Handler 方法,用于根据 ID 检索 Student 对象:

@RestController
@RequestMapping(value = "/api")
public class StudentRestController {

    @GetMapping("/student/{id}")
    public ResponseEntity<Student> get(@PathVariable("id") int id) {
        // 自定义逻辑
        return ResponseEntity.ok(new Student(id, "John", "Wiliams", "AA"));
     }
}

现在,使用 CURL 向 http://localhost:8080/api/student/1 发送一个请求:

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

端点响应如下:

{"timestamp":"2021-02-14T14:54:19.426+00:00","status":500,"error":"Internal Server Error","message":"","path":"/api/student/1"}

查看服务器日志,Spring 抛出了 HttpMessageNotWritableException 异常:

[org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.baeldung.boot.noconverterfound.model.Student]

最后,创建一个测试用例,看看当 Student 类中没有定义 getter 方法时,Spring 会如何处理:

@RunWith(SpringRunner.class)
@WebMvcTest(StudentRestController.class)
public class NoConverterFoundIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void whenGettersNotDefined_thenThrowException() throws Exception {

        String url = "/api/student/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 found for return value of type"));
    }
}

4、解决办法

为了避免该异常,最常见的解决方案之一是为返回对象的每个属性定义一个 Getter 方法。

因此,在 Student 类中添加 getter 方法,并创建一个新的测试用例来验证一切是否能按预期运行:

@Test
public void whenGettersAreDefined_thenReturnObject() throws Exception {

    String url = "/api/student/2";

    this.mockMvc.perform(get(url))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$.firstName").value("John"));
}

一个不明智的解决方案是直接使用 public 属性。然而,这并不是一个百分之百安全的方法,因为它违背了一些最佳实践。

5、总结

本文介绍了 Spring 抛出 org.springframework.http.converter.HttpMessageNotWritableException: "No converter found for return value of type" 的原因以及解决办法。


Ref:https://www.baeldung.com/spring-no-converter-found