Http Конвертеры сообщений с Spring Framework

Конвертеры сообщений Http с помощью Spring Framework

1. обзор

В этой статье описываетсяhow to Configure HttpMessageConverters in Spring.

Проще говоря, мы можем использовать конвертеры сообщений для маршаллового и демаршаллового Java-объектов в и из JSON, XML и т. Д. По HTTP.

Дальнейшее чтение:

Spring MVC Content Congotiation

Руководство по настройке согласования содержимого в приложении Spring MVC, а также по включению и отключению различных доступных стратегий.

Read more

Возврат данных изображения / мультимедиа с помощью Spring MVC

В статье показаны альтернативы для возврата изображения (или другого носителя) с помощью Spring MVC и обсуждены преимущества и недостатки каждого подхода.

Read more

Форматы двоичных данных в Spring REST API

В этой статье мы рассмотрим, как настроить механизм Spring REST для использования двоичных форматов данных, которые мы иллюстрируем с помощью Kryo. Кроме того, мы покажем, как поддерживать несколько форматов данных с помощью буферов протокола Google.

Read more

2. Основы

2.1. Включить Web MVC

Для начала веб-приложение должно бытьconfigured with Spring MVC support.. Удобный и очень настраиваемый способ сделать это - использовать аннотацию@EnableWebMvc:

@EnableWebMvc
@Configuration
@ComponentScan({ "com.example.web" })
public class WebConfig implements WebMvcConfigurer {
    ...
}

Обратите внимание, что этот класс реализуетWebMvcConfigurer, что позволит нам изменить список Http-преобразователей по умолчанию на наш собственный.

2.2. Конвертеры сообщений по умолчанию

По умолчанию следующие экземплярыHttpMessageConverters предварительно включены:

  • ByteArrayHttpMessageConverter - конвертирует байтовые массивы

  • StringHttpMessageConverter - конвертирует строки

  • ResourceHttpMessageConverter - конвертируетorg.springframework.core.io.Resource для любого типа потока октетов

  • SourceHttpMessageConverter - конвертируетjavax.xml.transform.Source

  • FormHttpMessageConverter - конвертирует данные формы в / изMultiValueMap<String, String>.

  • Jaxb2RootElementHttpMessageConverter - конвертирует объекты Java в / из XML (добавляется, только если JAXB2 присутствует в пути к классам)

  • MappingJackson2HttpMessageConverter - конвертирует JSON (добавляется, только если в пути к классам присутствует Jackson 2) __

  • MappingJacksonHttpMessageConverter - конвертирует JSON (добавляется, только если Джексон присутствует в пути к классам)

  • AtomFeedHttpMessageConverter - конвертирует потоки Atom (добавляется, только если Rome присутствует в пути к классам)

  • RssChannelHttpMessageConverter - конвертирует RSS-каналы(added only if Rome is present on the classpath)

3. Связь клиент-сервер - только JSON

3.1. Согласование контента высокого уровня

Каждая реализацияHttpMessageConverter имеет один или несколько связанных типов MIME.

При получении нового запросаSpring will use the “Accept” header to determine the media type that it needs to respond with.

Затем он попытается найти зарегистрированный преобразователь, способный обрабатывать этот конкретный тип мультимедиа. Наконец, он будет использовать это для преобразования сущности и отправки ответа обратно.

Процесс аналогичен при получении запроса, который содержит информацию JSON. Платформа будет использовать заголовокuse the “*Content-Type », чтобы определить медиа-тип тела запроса *.

Затем он будет искатьHttpMessageConverter, который может преобразовать тело, отправленное клиентом, в объект Java.

Давайте проясним это на небольшом примере:

  • Клиент отправляет запрос GET на/foos с заголовкомAccept, установленным наapplication/json - чтобы получить все ресурсыFoo как JSON

  • Контроллер SpringFoo поражен и возвращает соответствующие объекты JavaFoo

  • Затем Spring использует один из конвертеров сообщений Джексона, чтобы маршалировать сущности в JSON.

Теперь давайте посмотрим на особенности того, как это работает - и как мы можем использовать аннотации@ResponseBody и @RequestBody.

3.2. @ResponseBodyс

@ResponseBody в методе контроллера указывает Spring, чтоthe return value of the method is serialized directly to the body of the HTTP Response. Как обсуждалось выше, заголовок «Accept», указанный клиентом, будет использоваться для выбора соответствующего преобразователя Http для маршалинга объекта.

Давайте посмотрим на простой пример:

@GetMapping("/{id}")
public @ResponseBody Foo findById(@PathVariable long id) {
    return fooService.findById(id);
}

Теперь клиент укажет заголовок «Принять» дляapplication/json в запросе - пример команды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 Response Body:

{
    "id": 1,
    "name": "Paul",
}

3.3. @RequestBodyс

Мы можем использовать аннотацию@RequestBody для аргумента метода контроллера, чтобы указатьthat the body of the HTTP Request is deserialized to that particular Java entity. Чтобы определить подходящий конвертер, 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. Конфигурация пользовательских преобразователей

Мы также можемcustomize the message converters by implementing the WebMvcConfigurer interface и переопределить методconfigureMessageConverters:

@EnableWebMvc
@Configuration
@ComponentScan({ "com.example.web" })
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(
      List> converters) {

        messageConverters.add(createXmlHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    private HttpMessageConverter createXmlHttpMessageConverter() {
        MarshallingHttpMessageConverter xmlConverter =
          new MarshallingHttpMessageConverter();

        XStreamMarshaller xstreamMarshaller = new XStreamMarshaller();
        xmlConverter.setMarshaller(xstreamMarshaller);
        xmlConverter.setUnmarshaller(xstreamMarshaller);

        return xmlConverter;
    }
}


И вот соответствующая конфигурация XML:



    
        
        
            
            
        
    


В этом примере мы создаем новый конвертер -MarshallingHttpMessageConverter - и используем поддержку Spring XStream для его настройки. Это обеспечивает большую гибкость, посколькуwe’re working with the low-level APIs of the underlying marshalling framework - в данном случае XStream - и мы можем настроить это, как захотим.

Обратите внимание, что этот пример требует добавления библиотеки XStream в путь к классам.

Также имейте в виду, что, расширяя этот класс поддержки,we’re losing the default message converters which were previously pre-registered.

Конечно, теперь мы можем сделать то же самое для Джексона - определив наши собственныеMappingJackson2HttpMessageConverter.. Теперь мы можем установить пользовательскийObjectMapper для этого конвертера и настроить его так, как нам нужно.

В этом случае XStream был выбранной реализацией маршаллера / демаршаллера, но также можно использоватьothers, напримерCastorMarshaller.

На данный момент - с включенным XML на серверной стороне - мы можем использовать API с представлениями XML:

curl --header "Accept: application/xml"
  http://localhost:8080/spring-boot-rest/foos/1

4.1. Поддержка Spring Boot

Если мы используем Spring Boot, мы можем избежать реализацииWebMvcConfigurer and, добавляя все конвертеры сообщений вручную, как мы делали выше.

Мы можем просто определить разные sbeansHttpMessageConverter в контексте, и Spring Boot автоматически добавит их в создаваемую автоконфигурацию:

@Bean
public HttpMessageConverter createXmlHttpMessageConverter() {
    MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

    // ...

    return xmlConverter;
}





5. ИспользованиеRestTemplate Spring с конвертерами сообщений HTTP

Как и на стороне сервера, преобразование сообщений Http можно настроить на стороне клиента на SpringRestTemplate.

Мы собираемся настроить шаблон с заголовками «Accept» и «Content-Type», когда это необходимо. Затем мы попытаемся использовать REST API с полным маршалингом и демаршалингом ресурсаFoo - как с 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

Теперь давайте явно извлечем ресурс как представление XML. Мы собираемся определить набор конвертеров и установить их наRestTemplate.

Поскольку мы используем XML, мы будем использовать тот же маршаллер XStream, что и раньше:

@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

Точно так же давайте теперь воспользуемся 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. Обновить ресурс с помощью XMLContent-Type

Наконец, давайте также отправим данные 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());
}

Интересно то, что мы можем смешивать типы носителей -we’re sending XML data but we’re waiting for JSON data back from the server. Это показывает, насколько мощным является механизм преобразования Spring.

6. Заключение

В этом руководстве мы рассмотрели, как Spring MVC позволяет нам указывать и полностью настраивать преобразователи сообщений Http дляautomatically marshall/unmarshall Java Entities to and from XML or JSON. Это, конечно, упрощенное определение, и механизм преобразования сообщений может сделать гораздо больше, как мы видим из последнего тестового примера.

Мы также рассмотрели, как использовать тот же мощный механизм с клиентомRestTemplate, что приводит к полностью типобезопасному способу использования API.

Как всегда, доступен код, представленный в этой статьеover on Github.