1. 概述
本教程將深入探討 Spring 中的 <em >HttpMessageNotWritableException: “No converter found for return value of type”</em > 異常。
首先,我們將解釋該異常的主要原因。然後,我們將深入研究如何使用一個實際案例通過生產該異常來解決問題,最後將探討如何修復它。
2. 導致原因
通常,此異常發生時,Spring 無法獲取返回對象的屬性。
此異常的最常見原因通常是返回對象沒有為其屬性提供任何公共 getter 方法。
Spring Boot 默認依賴 Jackson 庫來執行請求和響應對象的序列化/反序列化所有繁重的工作。
因此,另一個可能導致我們異常的原因是缺少或使用錯誤的 Jackson 依賴項。
簡而言之,對於此類異常的一般準則是檢查以下內容:
- 默認構造函數
- getter
- Jackson 依賴項
請注意,異常類型 已從 java.lang.IllegalArgumentException 變為 org.springframework.http.converter.HttpMessageNotWritableException.
3. 實際示例
現在,讓我們來看一個生成 <em>org.springframework.http.converter.HttpMessageNotWritableException</em> 的示例:“未找到與返回值類型對應的轉換器”。
為了演示一個實際應用案例,我們將使用 Spring Boot 構建一個基本的學生管理 REST API。
首先,讓我們 創建一個模型類 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
}其次,我們將創建一個 Spring 控制器,其中包含一個單一的 handler 方法,用於根據 學生對象的 id 檢索該對象:
@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"));
}
}現在,如果我們使用 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]最後,讓我們創建一個測試用例,以查看 Spring 在 getter 方法未定義在 Student 類中時的行為:
@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. 解決方案
最常見的防止異常的方法是為每個對象想要在 JSON 中返回的屬性定義一個獲取器方法。
因此,讓我們在 Student 類中添加獲取器方法,並創建一個新的測試用例來驗證一切是否按預期工作:
@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"));
}一個不建議採用的解決方案是公開屬性。然而,這並不是一個 100% 安全的方法,因為它違背了許多最佳實踐。
5. 結論
在本文中,我們解釋了導致 Spring 拋出 org.springframework.http.converter.HttpMessageNotWritableException: “未找到轉換器用於返回值類型” 的原因。
然後,我們討論瞭如何產生該異常以及如何在實踐中解決它。