1. Обзор
В этом руководстве мы собираемся пролить свет на исключение Spring HttpMessageNotWritableException: исключение «Не найден конвертер для возвращаемого значения типа»
.
Во-первых, мы объясним основные причины исключения. Затем мы копнем глубже, чтобы увидеть, как создать его на реальном примере и, наконец, как это исправить.
2. Причины
Обычно это исключение возникает, когда Spring не может получить свойства возвращаемого объекта.
Наиболее типичная причина этого исключения обычно заключается в том, что возвращаемый объект не имеет общедоступных методов получения для своих свойств .
По умолчанию Spring Boot использует библиотеку Jackson для выполнения всей тяжелой работы по сериализации/десериализации объектов запросов и ответов.
Итак, еще одной распространенной причиной нашего исключения может быть отсутствие или использование неправильных зависимостей Джексона .
Короче говоря, общая рекомендация для такого исключения состоит в том, чтобы проверить наличие:
- Конструктор по умолчанию
- Добытчики
- зависимости Джексона
Имейте в виду, что тип исключения изменился с java.lang.IllegalArgumentException
на org.springframework.http.converter.HttpMessageNotWritableException.
3. Практический пример
Теперь давайте посмотрим на пример, который генерирует org.springframework.http.converter.HttpMessageNotWritableException
: «Не найден конвертер для возвращаемого значения типа».
Чтобы продемонстрировать реальный пример использования, мы собираемся создать базовый REST API для управления учащимися с помощью Spring Boot .
Во-первых, давайте создадим наш модельный класс Student
и притворимся, что забыли сгенерировать методы получения :
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 с одним методом-обработчиком для извлечения объекта 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"));
}
}
Теперь, если мы отправим запрос на http://localhost:8080/api/student/1
с помощью CURL :
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.foreach.boot.noconverterfound.model.Student]
Наконец, давайте создадим тестовый пример, чтобы увидеть, как Spring ведет себя, когда методы получения не определены в классе 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: «Не найден конвертер для возвращаемого значения типа»
.
Затем мы обсудили, как создать исключение и как обращаться с ним на практике.
Как всегда, полный исходный код примеров доступен на GitHub .