1. Обзор
В этой статье описывается как настроить HttpMessageConverters весной .
Проще говоря, мы можем использовать конвертеры сообщений для маршалла и немаршалла объектов Java в JSON, XML и т. Д. И из них – по HTTP.
Дальнейшее чтение:
Согласование содержимого Spring MVC
Возврат данных изображения/мультимедиа с помощью Spring MVC
Двоичные форматы данных в API Spring REST
2. Основы
2.1. EnableWebMvc
Для начала веб-приложение должно быть настроено с поддержкой Spring MVC. Удобный и очень настраиваемый способ сделать это-использовать аннотацию @EnableWebMvc :
@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
...
}Обратите внимание, что этот класс реализует WebMvcConfigurer – который позволит нам изменить список Http-конвертеров по умолчанию с помощью нашего собственного.
2.2. Конвертеры Сообщений По Умолчанию
По умолчанию используется следующее Экземпляры HttpMessageConverter s предварительно включены:
- ByteArrayHttpMessageConverter – преобразует массивы байтов
- StringHttpMessageConverter – преобразует строки
- ResourceHttpMessageConverter – преобразует org.springframework.core.io.Resource для любого типа потока октетов
- SourceHttpMessageConverter – преобразует javax.xml.transform.Источник
- FormHttpMessageConverter – преобразует данные формы в/из многозначной карты String> . String>
- . Jaxb2RootElementHttpMessageConverter
- – преобразует объекты Java в/из XML (добавляется только в том случае, если JAXB2 присутствует в пути к классу) MappingJackson2HttpMessageConverter
- – преобразует JSON (добавляется только в том случае, если Jackson 2 присутствует в пути к классу) MappingJacksonHttpMessageConverter
- – преобразует JSON (добавляется только в том случае, если Джексон присутствует в пути к классу) AtomFeedHttpMessageConverter
- – преобразует каналы Atom (добавляется только в том случае, если в пути к классу присутствует Rome) RssChannelHttpMessageConverter – преобразует RSS-каналы
3. Связь Клиент-Сервер – Только JSON
3.1. Согласование Контента На Высоком Уровне
Каждая реализация HttpMessageConverter имеет один или несколько связанных типов MIME.
При получении нового запроса Spring будет использовать заголовок “ Accept “, чтобы определить тип носителя, на который он должен ответить .
Затем он попытается найти зарегистрированный конвертер, способный обрабатывать этот конкретный тип носителя. Наконец, он будет использовать это для преобразования сущности и отправки ответа.
Процесс аналогичен при получении запроса, содержащего информацию JSON. Фреймворк будет использовать заголовок “ Content-Type ” для определения типа носителя тела запроса .
Затем он будет искать HttpMessageConverter , который может преобразовать тело, отправленное клиентом, в объект Java.
Давайте проясним это с помощью краткого примера:
- клиент отправляет запрос GET в /foo с заголовком Accept , установленным в application/json – для получения всех Foo ресурсов в формате JSON
- контроллер Foo Spring нажат и возвращает соответствующие объекты Foo Java
- Затем Spring использует один из преобразователей сообщений Джексона для преобразования сущностей в JSON
Давайте теперь рассмотрим особенности того, как это работает – и как мы можем использовать аннотации @ResponseBody и @ RequestBody .
3.2. @ResponseBody
@ResponseBody в методе контроллера указывает Spring, что возвращаемое значение метода сериализуется непосредственно в тело HTTP-ответа . Как обсуждалось выше, заголовок ” Accept “, указанный Клиентом, будет использоваться для выбора соответствующего Http-конвертера для управления сущностью.
Давайте рассмотрим простой пример :
@GetMapping("/{id}")
public @ResponseBody Foo findById(@PathVariable long id) {
return fooService.findById(id);
}Теперь клиент укажет заголовок “Принять” в application/json в команде request – example curl :
curl --header "Accept: application/json" http://localhost:8080/spring-boot-rest/foos/1
Класс Foo :
public class Foo {
private long id;
private String name;
}И Тело ответа Http:
{
"id": 1,
"name": "Paul",
}3.3. @RequestBody
Мы можем использовать @RequestBody аннотацию в аргументе метода контроллера, чтобы указать , что тело HTTP-запроса десериализуется в эту конкретную сущность Java . Чтобы определить соответствующий конвертер, Spring будет использовать заголовок “Content-Type” из запроса клиента.
Давайте рассмотрим пример:
@PutMapping("/{id}")
public @ResponseBody void update(@RequestBody Foo foo, @PathVariable String id) {
fooService.update(foo);
}Далее, давайте потребим это с помощью объекта JSON – мы указываем “Content-Type “ , чтобы быть application/json :
curl -i -X PUT -H "Content-Type: application/json"
-d '{"id":"83","name":"klik"}' http://localhost:8080/spring-boot-rest/foos/1Мы получаем ответ 200 OK – успешный ответ:
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Length: 0 Date: Fri, 10 Jan 2014 11:18:54 GMT
4. Настройка Пользовательских Конвертеров
Мы также можем настроить преобразователи сообщений, реализовав WebMvcConfigurer интерфейс и переопределив метод configureMessageConverters :
@EnableWebMvc
@Configuration
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(
List> converters) {
messageConverters.add(createXmlHttpMessageConverter());
messageConverters.add(new MappingJackson2HttpMessageConverter());
}
private HttpMessageConverter В этом примере мы создаем новый конвертер – MarshallingHttpMessageConverter – и используем поддержку Spring XStream для его настройки. Это обеспечивает большую гибкость, так как мы работаем с низкоуровневыми API базовой платформы маршалинга -в данном случае XStream – и мы можем настроить это так, как захотим.
Обратите внимание, что в этом примере требуется добавить библиотеку XStream в путь к классу.
Также имейте в виду, что, расширяя этот класс поддержки, мы теряем конвертеры сообщений по умолчанию, которые ранее были предварительно зарегистрированы.
Конечно, теперь мы можем сделать то же самое для Джексона – определив наш собственный MappingJackson2HttpMessageConverter. Теперь мы можем установить пользовательский ObjectMapper на этом конвертере и настроить его так, как нам нужно.
В этом случае XStream был выбран реализацией marshaller/unmarshaller, но другие как CastorMarshaller также могут быть использованы.
На этом этапе – с включенным XML на задней панели – мы можем использовать API с XML-представлениями:
curl --header "Accept: application/xml" http://localhost:8080/spring-boot-rest/foos/1
4.1. Поддержка Пружинной загрузки
Если мы используем Spring Boot, мы можем избежать реализации WebMvcConfigurer и добавления всех конвертеров сообщений вручную, как мы делали выше.
Мы можем просто определить разные HttpMessageConverter бобы в контексте, и Spring Boot автоматически добавит их в созданную им автоконфигурацию:
@Bean public HttpMessageConverter
5. Использование RestTemplate Spring С Преобразователями Http-Сообщений
Как и на стороне сервера, преобразование Http-сообщений может быть настроено на стороне клиента на Spring RestTemplate .
Мы собираемся настроить шаблон с заголовками ” Accept ” и ” Content-Type “, когда это необходимо. Затем мы попытаемся использовать REST API с полным маршалингом и безмаршалингом ресурса Food – как с JSON, так и с XML.
5.1. Получение Ресурса Без заголовка Accept
@Test
public void testGetFoo() {
String URI = "http://localhost:8080/spring-boot-rest/foos/{id}";
RestTemplate restTemplate = new RestTemplate();
Foo foo = restTemplate.getForObject(URI, Foo.class, "1");
Assert.assertEquals(new Integer(1), foo.getId());
}5.2. Получение Ресурса С Заголовком application/xml Accept
Теперь давайте явно извлекем ресурс в виде XML-представления. Мы определим набор преобразователей и установим их на RestTemplate.
Поскольку мы потребляем XML, мы будем использовать тот же XStreammarshaller, что и раньше:
@Test
public void givenConsumingXml_whenReadingTheFoo_thenCorrect() {
String URI = BASE_URI + "foos/{id}";
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_XML));
HttpEntity entity = new HttpEntity(headers);
ResponseEntity response =
restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
Foo resource = response.getBody();
assertThat(resource, notNullValue());
}
private List> getMessageConverters() {
XStreamMarshaller marshaller = new XStreamMarshaller();
MarshallingHttpMessageConverter marshallingConverter =
new MarshallingHttpMessageConverter(marshaller);
List> converters =
ArrayList>();
converters.add(marshallingConverter);
return converters;
} 5.3. Получение ресурса С Заголовком application/json Accept
Аналогично, давайте теперь потребим REST API, запросив JSON:
@Test
public void givenConsumingJson_whenReadingTheFoo_thenCorrect() {
String URI = BASE_URI + "foos/{id}";
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity entity = new HttpEntity(headers);
ResponseEntity response =
restTemplate.exchange(URI, HttpMethod.GET, entity, Foo.class, "1");
Foo resource = response.getBody();
assertThat(resource, notNullValue());
}
private List> getMessageConverters() {
List> converters =
new ArrayList>();
converters.add(new MappingJackson2HttpMessageConverter());
return converters;
} 5.4. Обновление Ресурса С Типом Содержимого XML
Наконец, давайте также отправим данные JSON в REST API и укажем тип носителя этих данных через заголовок Content-Type :
@Test
public void givenConsumingXml_whenWritingTheFoo_thenCorrect() {
String URI = BASE_URI + "foos/{id}";
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
Foo resource = new Foo(4, "jason");
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType((MediaType.APPLICATION_XML));
HttpEntity entity = new HttpEntity(resource, headers);
ResponseEntity response = restTemplate.exchange(
URI, HttpMethod.PUT, entity, Foo.class, resource.getId());
Foo fooResponse = response.getBody();
Assert.assertEquals(resource.getId(), fooResponse.getId());
} Что интересно здесь, так это то, что мы можем смешивать типы носителей – мы отправляем XML-данные, но ждем данных JSON с сервера . Это показывает, насколько мощным на самом деле является механизм преобразования пружины.
6. Заключение
В этом уроке мы рассмотрели, как Spring MVC позволяет нам указывать и полностью настраивать HttpMessageConverters для автоматического маршалла/удаления объектов Java в XML или JSON и из них . Это, конечно, упрощенное определение, и механизм преобразования сообщений может сделать гораздо больше, как мы видим из последнего тестового примера.
Мы также рассмотрели, как использовать тот же мощный механизм с RestTemplate клиентом, что приведет к полностью типобезопасному способу использования API.
Как всегда, код, представленный в этой статье, доступен на Github .