Spring MVC - При обращении к RestController, возникает org.springframework.http.converter.HttpMessageNotWritableException
Пишу web-приложение на Spring MVC (5.2.8). Создал REST-контроллер, следующего вида:
@RestController
@RequestMapping("message")
public class MessageController {
private List<Map<String, String>> messages = new ArrayList<Map<String, String>>() {{
add(new HashMap<String, String>() {{ put("id", "1"); put("text", "First message"); }});
add(new HashMap<String, String>() {{ put("id", "2"); put("text", "Second message"); }});
add(new HashMap<String, String>() {{ put("id", "3"); put("text", "Third message"); }});
}};
@GetMapping
@ResponseBody
public List<Map<String, String>> list() {
return messages;
}
}
Конфигурацию web-приложения делаю при помощи "Java Config", файл WebConfig.java имеет следующий вид:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ru.egar")
public class WebConfig implements WebMvcConfigurer {
private final ApplicationContext applicationContext;
@Autowired
public WebConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
Приложение успешно запускается на Tomcat. В браузере при обращении по адресу: http://localhost:8080/message получаю ошибку вида:
Подскажите, какой конфигурации не хватает для корректного отображения JSON-ответа?
Ответы (1 шт):
После "многочасового гуглежа" нашёл ответ на свой вопрос. Решение следующее:
Добавляем зависимость (в случае Maven - в pom.xml):
com.fasterxml.jackson.core jackson-databind 2.11.2В файле Spring-конфигурации (в моём случае WebConfig.java) дополнительно описываем четыре метода:
- @Bean mappingJackson2HttpMessageConverter()
- @Override void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
- @Bean ObjectMapper objectMapper()
- @Override void configureMessageConverters(List<HttpMessageConverter<?>> converters)
По итогу мой файл WebConfig.java выглядит следующим образом:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ru.egar")
public class WebConfig implements WebMvcConfigurer {
private final ApplicationContext applicationContext;
@Autowired
public WebConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter =
new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper());
return mappingJackson2HttpMessageConverter;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objMapper = new ObjectMapper();
objMapper.enable(SerializationFeature.INDENT_OUTPUT);
objMapper.setSerializationInclusion(
JsonInclude.Include.NON_NULL);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
objMapper.setDateFormat(df);
return objMapper;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
}
}
