1. Введение
Тип содержимого указывает, как интерпретировать данные, присутствующие в запросе/ответе. Всякий раз, когда контроллер получает веб-запрос, он потребляет или создает некоторые типы мультимедиа. В этой модели «запрос-ответ» можно потреблять/производить несколько типов мультимедиа, и JSON является одним из них.
В этом кратком руководстве мы рассмотрим различные способы установки типа контента в Spring MVC.
2. @RequestMapping
весной
Проще говоря, @RequestMapping
— это важная аннотация, которая сопоставляет веб-запросы с контроллером Spring. Он имеет различные атрибуты, включая метод HTTP, параметры запроса, заголовки и типы мультимедиа.
Как правило, типы носителей делятся на две категории: расходуемые и производимые. Помимо этих двух, мы также можем определить собственный тип носителя в Spring . Основная цель состоит в том, чтобы ограничить первичное сопоставление списком типов мультимедиа для нашего обработчика запросов.
2.1. Тип расходного материала
С помощью атрибута потребления
мы указываем тип носителя, который контроллер будет принимать от клиента. Мы также можем предоставить список типов носителей. Давайте определим простую конечную точку:
@RequestMapping(value = "/greetings", method = RequestMethod.POST, consumes="application/json")
public void addGreeting(@RequestBody ContentType type, Model model) {
// code here
}
Если клиент указывает тип носителя, который не может быть использован ресурсом, система выдаст ошибку HTTP «415 Unsupported Media Type».
2.2. Тип производимого носителя
В отличие от атрибута
« использует», атрибут « products
» определяет тип мультимедиа, который ресурс может создавать и отправлять обратно клиенту. Без сомнения, мы можем использовать список вариантов. Если ресурс не может создать запрошенный ресурс, система выдаст ошибку HTTP «406 Not Acceptable».
Давайте начнем с простого примера — API, предоставляющий строку JSON.
Вот наша конечная точка:
@RequestMapping(
value = "/greetings-with-response-body",
method = RequestMethod.GET,
produces="application/json"
)
@ResponseBody
public String getGreetingWhileReturnTypeIsString() {
return "{\"test\": \"Hello using @ResponseBody\"}";
}
Давайте проверим это с помощью CURL:
curl http://localhost:8080/greetings-with-response-body
Приведенная выше команда выдает ответ:
{ "test": "Hello using @ResponseBody" }
В зависимости от типа содержимого, представленного в заголовке, @ResponseBody
только привязывает возвращаемое значение метода к телу веб-ответа.
3. Content-Type установлен неправильно
Когда метод имеет тип возвращаемого значения String,
а JSON Mapper отсутствует в пути к классам. В этом случае возвращаемое значение обрабатывается классом StringHttpMessageConverter
, который устанавливает тип содержимого «текстовый/обычный». Это часто приводит к проблеме, когда контроллер не может создать ожидаемый тип контента.
Рассмотрим разные подходы к решению этой проблемы.
3.1. Использование @ ResponseBody
с JSON Mapper
Класс Jackson ObjectMapper
анализирует JSON из строки, потока или файла. Если Джексон находится в пути к классам, любой контроллер в приложениях Spring по умолчанию отображает ответ JSON.
Чтобы включить Джексона в путь к классам, нам нужно добавить следующую зависимость в pom.xml
:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
Давайте добавим модульный тест для проверки из ответа:
@Test
public void givenReturnTypeIsString_whenJacksonOnClasspath_thenDefaultContentTypeIsJSON()
throws Exception {
// Given
String expectedMimeType = "application/json";
// Then
String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-body", 1))
.andReturn().getResponse().getContentType();
Assert.assertEquals(expectedMimeType, actualMimeType);
}
3.2. Использование ResponseEntiy
В отличие от @ResponseBody
, ResponseEntity
является универсальным типом, представляющим весь HTTP-ответ. В результате мы можем контролировать все, что в него входит: код состояния, заголовок и тело.
Давайте определим новую конечную точку:
@RequestMapping(
value = "/greetings-with-response-entity",
method = RequestMethod.GET,
produces = "application/json"
)
public ResponseEntity<String> getGreetingWithResponseEntity() {
final HttpHeaders httpHeaders= new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<String>("{\"test\": \"Hello with ResponseEntity\"}", httpHeaders, HttpStatus.OK);
}
В консоли разработчика нашего браузера мы видим следующий ответ:
{"test": "Hello with ResponseEntity"}
С ResponseEntity
у нас должен быть управляемый аннотациями тег в нашем диспетчерском сервлете :
<mvc:annotation-driven />
Проще говоря, приведенный выше тег дает больший контроль над внутренней работой Spring MVC .
Давайте проверим тип содержимого ответа с помощью тестового примера:
@Test
public void givenReturnTypeIsResponseEntity_thenDefaultContentTypeIsJSON() throws Exception {
// Given
String expectedMimeType = "application/json";
// Then
String actualMimeType = this.mockMvc.perform(MockMvcRequestBuilders.get("/greetings-with-response-entity", 1))
.andReturn().getResponse().getContentType();
Assert.assertEquals(expectedMimeType, actualMimeType);
}
3.3. Использование возвращаемого типа Map<String, Object>
И последнее, но не менее важное: мы также можем установить тип содержимого, изменив тип возвращаемого значения со String
на Map
. Этот тип возвращаемого значения Map
потребует маршалинга и возвращает объект JSON.
Вот наша новая конечная точка:
@RequestMapping(
value = "/greetings-with-map-return-type",
method = RequestMethod.GET,
produces = "application/json"
)
@ResponseBody
public Map<String, Object> getGreetingWhileReturnTypeIsMap() {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("test", "Hello from map");
return map;
}
Давайте посмотрим на это в действии:
curl http://localhost:8080/greetings-with-map-return-type
Команда curl возвращает ответ JSON:
{ "test": "Hello from map" }
4. Вывод
В этой статье объясняется, как установить тип содержимого в Spring MVC, сначала добавив сопоставитель Json в путь к классам, затем используя ResponseEntity и, наконец, изменив тип возвращаемого значения со String на Map.
Как всегда, все фрагменты кода можно найти на GitHub .