1. Overview
1.概述
In this tutorial, we’re going to shed light on Spring’s HttpMessageNotWritableException: “No converter found for return value of type” exception.
在本教程中,我们将阐明Spring的HttpMessageNotWritableException。”没有找到类型的返回值的转换器”异常。
First, we’ll explain the main causes of the exception. Then, we’ll dig deeper to see how to produce it using a real-world example and finally how to fix it.
首先,我们将解释导致异常的主要原因。然后,我们将深入挖掘,用一个真实世界的例子来看看如何产生它,最后是如何修复它。
2. The Causes
2.原因
Typically, this exception occurs when Spring fails to fetch the properties of a returned object.
通常情况下,当Spring无法获取返回对象的属性时就会出现这种异常。
The most typical cause of this exception is usually that the returned object doesn’t have any public getter methods for its properties.
导致这种异常的最典型的原因通常是:返回的对象没有任何用于其属性的公共getter方法。
By default, Spring Boot relies on the Jackson library to do all the heavy lifting of serializing/deserializing request and response objects.
默认情况下,Spring Boot依靠Jackson库来完成序列化/反序列化请求和响应对象的所有繁重工作。
So, another common cause of our exception could be missing or using the wrong Jackson dependencies.
因此,另一个导致我们的异常的常见原因可能是缺少或使用了错误的Jackson依赖项。
In short, the general guideline for such an exception is to check for the presence of:
简而言之,这种例外情况的一般准则是检查是否存在。
- Default constructor
- Getters
- Jackson dependencies
Please bear in mind that the exception type has changed from java.lang.IllegalArgumentException to org.springframework.http.converter.HttpMessageNotWritableException.
请记住,异常类型已经从java.lang.IllegalArgumentException变为org.springframework.http.converter.HttpMessageNotWritableException.。
3. Practical Example
3.实例
Now, let’s see an example that generates org.springframework.http.converter.HttpMessageNotWritableException: “No converter found for return value of type”.
现在,让我们看看一个产生org.springframework.http.converter.HttpMessageNotWritableException的例子:”没有找到返回值类型的转换器”。
To demonstrate a real-world use case, we’re going to build a basic REST API for student management using Spring Boot.
为了演示一个真实的用例,我们将使用Spring Boot构建一个用于学生管理的基本REST API。
Firstly, let’s create our model class Student and pretend to forget to generate the getter methods:
首先,让我们创建我们的模型类Student并假装忘记生成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;
}
// Setters
}
Secondary, we’ll create a Spring controller with a single handler method to retrieve a Student object by its id:
其次,我们将创建一个Spring控制器,其中有一个处理方法,通过id检索一个Student对象。
@RestController
@RequestMapping(value = "/api")
public class StudentRestController {
@GetMapping("/student/{id}")
public ResponseEntity<Student> get(@PathVariable("id") int id) {
// Custom logic
return ResponseEntity.ok(new Student(id, "John", "Wiliams", "AA"));
}
}
Now, if we send a request to http://localhost:8080/api/student/1 using CURL:
现在,如果我们使用CURL向http://localhost:8080/api/student/1发送一个请求。
curl http://localhost:8080/api/student/1
The endpoint will send back this response:
端点将发回这个响应。
{"timestamp":"2021-02-14T14:54:19.426+00:00","status":500,"error":"Internal Server Error","message":"","path":"/api/student/1"}
Looking at the logs, Spring threw the HttpMessageNotWritableException:
看一下日志,Spring抛出了HttpMessageNotWritableException。
[org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.baeldung.boot.noconverterfound.model.Student]
Finally, let’s create a test case to see how Spring behaves when the getter methods are not defined in the Student class:
最后,让我们创建一个测试案例,看看当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. The Solution
4.解决方案
One of the most common solutions to prevent the exception is to define a getter method for each object’s property we want to return in JSON.
最常见的解决方案之一是为我们想要在JSON中返回的每个对象的属性定义一个getter方法来防止异常。
So, let’s add the getter methods in the Student class and create a new test case to verify if everything will work as expected:
因此,让我们在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"));
}
An ill-advised solution would be making the properties public. However, this is not a 100% safe approach as it goes against several best practices.
一个不明智的解决方案是将这些属性公开。然而,这并不是一个100%安全的方法,因为它违背了一些最佳实践。
5. Conclusion
5.总结
In this short article, we explained what causes Spring to throw org.springframework.http.converter.HttpMessageNotWritableException: ” No converter found for return value of type”.
在这篇短文中,我们解释了导致Spring抛出org.springframework.http.converter.HttpMessageNotWritableException:” 没有找到返回值类型的转换器”。
Then, we discussed how to produce the exception and how to address it in practice.
然后,我们讨论了如何产生例外情况以及如何在实践中解决这个问题。
As always, the full source code of the examples is available over on GitHub.
一如既往,这些示例的完整源代码可在GitHub上获得over。