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