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)); HttpEntityentity = 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)); HttpEntityentity = 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)); HttpEntityentity = 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 .