1、概览 {#1概览}
本文将带你了解 Spring 中出现 HttpMessageNotWritableException: "No converter found for return value of type"
异常的原因以及解决办法。
2、原因 {#2原因}
通常,当 Spring 无法获取返回对象的属性时,就会出现这种异常。
导致这种异常的最典型原因通常是返回对象的属性没有任何 public
getter 方法。
默认情况下,Spring Boot 依赖于 Jackson 库来完成序列化/反序列化请求和响应对象的所有工作。
因此,导致异常的另一个常见原因可能是 缺少或使用了错误的 Jackson 依赖。
简而言之,这种异常情况的一般准则是检查是否存在以下情况:
- 默认构造器
- Getter 方法
- Jackson 依赖
注意,异常类型 已从 java.lang.IllegalArgumentException
变为 org.springframework.http.converter.HttpMessageNotWritableException
。
3、实例 {#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、解决办法 {#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、总结 {#5总结}
本文介绍了 Spring 抛出 org.springframework.http.converter.HttpMessageNotWritableException: "No converter found for return value of type"
的原因以及解决办法。
Ref:https://www.baeldung.com/spring-no-converter-found