1. Обзор
В этой короткой статье мы подробно рассмотрим исключение Spring «HttpMessageNotWritableException: нет преобразователя для [класса…] с предустановленным Content-Type»
.
Во-первых, мы прольем свет на основную причину исключения. Затем мы спустимся в кроличью нору, чтобы увидеть, как воспроизвести ее на практическом примере и, наконец, как ее решить.
2. Причина
Прежде чем углубляться в детали, давайте попробуем понять, что означает исключение.
Трассировка стека исключения говорит сама за себя: она сообщает нам, что Spring не может найти подходящий HttpMessageConverter,
способный преобразовать объект Java в ответ HTTP .
По сути, Spring полагается на заголовок «Accept»
для определения типа носителя, на который он должен ответить.
Таким образом, использование типа носителя без предварительно зарегистрированного преобразователя сообщений приведет к сбою Spring с `` исключением .
3. Воспроизведение исключения
Теперь, когда мы знаем, что заставляет Spring генерировать наше исключение, давайте посмотрим, как воспроизвести его на практическом примере.
Давайте создадим метод-обработчик и притворимся, что указываем тип мультимедиа (для ответа), который не имеет зарегистрированного HttpMessageConverter
.
Например, давайте использовать APPLICATION_XML_VALUE
или «application/xml»
:
@GetMapping(value = "/student/v3/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Student> getV3(@PathVariable("id") int id) {
return ResponseEntity.ok(new Student(id, "Robert", "Miller", "BB"));
}
Далее отправим запрос на http://localhost:8080/api/student/v3/1
и посмотрим, что получится:
curl http://localhost:8080/api/student/v3/1
Конечная точка отправляет ответ:
{"timestamp":"2022-02-01T18:23:37.490+00:00","status":500,"error":"Internal Server Error","path":"/api/student/v3/1"}
Действительно, глядя на журналы, Spring выдает ошибку HttpMessageNotWritableException
:
[org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.foreach.boot.noconverterfound.model.Student] with preset Content-Type 'null']
Таким образом, возникает исключение, потому что нет HttpMessageConverter,
способного маршалировать и демаршалировать объекты Student
в и из XML .
Наконец, давайте создадим тестовый пример, чтобы убедиться, что Spring генерирует исключение HttpMessageNotWritableException
с указанным сообщением:
@Test
public void whenConverterNotFound_thenThrowException() throws Exception {
String url = "/api/student/v3/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 for [class com.foreach.boot.noconverterfound.model.Student] with preset Content-Type"));
}
4. Решение
Исправить это исключение можно только одним способом — использовать медиа-тип, имеющий зарегистрированный преобразователь сообщений.
Spring Boot использует автоматическую настройку для регистрации встроенных преобразователей сообщений .
Например, он автоматически зарегистрирует MappingJackson2HttpMessageConverter
, если в classpath присутствует зависимость Jackson 2 .
С учетом сказанного и зная, что Spring Boot включает Джексона в веб-стартер , давайте создадим новую конечную точку с типом носителя APPLICATION_JSON_VALUE :
@GetMapping(value = "/student/v2/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Student> getV2(@PathVariable("id") int id) {
return ResponseEntity.ok(new Student(id, "Kevin", "Cruyff", "AA"));
}
Теперь давайте создадим тестовый пример, чтобы убедиться, что все работает как за исключением:
@Test
public void whenJsonConverterIsFound_thenReturnResponse() throws Exception {
String url = "/api/student/v2/1";
this.mockMvc.perform(get(url))
.andExpect(status().isOk())
.andExpect(content().json("{'id':1,'firstName':'Kevin','lastName':'Cruyff', 'grade':'AA'}"));
}
Как мы видим, Spring не выбрасывает HttpMessageNotWritableException
благодаря MappingJackson2HttpMessageConverter
, который обрабатывает преобразование объекта Student
в JSON под капотом.
5. Вывод
В этом коротком уроке мы подробно обсудили, что заставляет Spring выбрасывать «HttpMessageNotWritableException No convertor for [class…] с предустановленным Content-Type»
.
Попутно мы продемонстрировали, как создать исключение и как исправить его на практике.
Как всегда, полный исходный код примеров доступен на GitHub .