HTTP-Nachrichtenkonverter mit dem Spring Framework

HTTP-Nachrichtenkonverter mit dem Spring Framework

1. Überblick

Dieser Artikel beschreibthow to Configure HttpMessageConverters in Spring.

Einfach ausgedrückt, wir können Nachrichtenkonverter verwenden, um Java-Objekte von und nach JSON, XML usw. über HTTP zu marshallen und zu unmarshallen.

Weitere Lektüre:

Spring MVC Content Negotiation

Eine Anleitung zum Konfigurieren der Inhaltsaushandlung in einer Spring MVC-Anwendung sowie zum Aktivieren und Deaktivieren der verschiedenen verfügbaren Strategien.

Read more

Zurückgeben von Bild- / Mediendaten mit Spring MVC

Der Artikel zeigt die Alternativen für die Rückgabe von Bildern (oder anderen Medien) mit Spring MVC und erläutert die Vor- und Nachteile der einzelnen Ansätze.

Read more

Binärdatenformate in einer Spring REST-API

In diesem Artikel erfahren Sie, wie Sie den Spring REST-Mechanismus so konfigurieren, dass er binäre Datenformate verwendet, die wir mit Kryo veranschaulichen. Darüber hinaus zeigen wir, wie mehrere Datenformate mit Google Protocol-Puffern unterstützt werden.

Read more

2. Die Grundlagen

2.1. Aktivieren Sie Web MVC

Zu Beginn muss die Webanwendungconfigured with Spring MVC support. sein. Eine bequeme und sehr anpassbare Möglichkeit, dies zu tun, besteht darin, die Annotation@EnableWebMvc zu verwenden:

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

Beachten Sie, dass diese KlasseWebMvcConfigurer implementiert, wodurch wir die Standardliste der HTTP-Konverter durch unsere eigene Liste ändern können.

2.2. Die Standardnachrichtenkonverter

Standardmäßig sind die folgendenHttpMessageConverters-Instanzen voraktiviert:

  • ByteArrayHttpMessageConverter - konvertiert Byte-Arrays

  • StringHttpMessageConverter - konvertiert Strings

  • ResourceHttpMessageConverter - konvertiertorg.springframework.core.io.Resource für jede Art von Oktettstrom

  • SourceHttpMessageConverter - konvertiertjavax.xml.transform.Source

  • FormHttpMessageConverter - konvertiert Formulardaten in / vonMultiValueMap<String, String>.

  • Jaxb2RootElementHttpMessageConverter - konvertiert Java-Objekte in / aus XML (wird nur hinzugefügt, wenn JAXB2 im Klassenpfad vorhanden ist).

  • MappingJackson2HttpMessageConverter - konvertiert JSON (wird nur hinzugefügt, wenn Jackson 2 im Klassenpfad vorhanden ist) __

  • MappingJacksonHttpMessageConverter - konvertiert JSON (wird nur hinzugefügt, wenn Jackson im Klassenpfad vorhanden ist)

  • AtomFeedHttpMessageConverter - konvertiert Atom-Feeds (nur hinzugefügt, wenn Rom im Klassenpfad vorhanden ist)

  • RssChannelHttpMessageConverter - konvertiert RSS-Feeds(added only if Rome is present on the classpath)

3. Client-Server-Kommunikation - Nur JSON

3.1. Verhandlung von Inhalten auf hoher Ebene

JederHttpMessageConverter-Implementierung sind ein oder mehrere MIME-Typen zugeordnet.

Beim Empfang einer neuen Anfrage wirdSpring will use the “Accept” header to determine the media type that it needs to respond with.

Anschließend wird versucht, einen registrierten Konverter zu finden, der diesen bestimmten Medientyp verarbeiten kann. Schließlich wird dies verwendet, um die Entität zu konvertieren und die Antwort zurückzusenden.

Der Vorgang ist ähnlich wie beim Empfangen einer Anfrage, die JSON-Informationen enthält. Das Framework verwendet den Headeruse the “*Content-Type, um den Medientyp des Anforderungshauptteils * zu bestimmen.

Anschließend wird nachHttpMessageConverter gesucht, mit denen der vom Client gesendete Text in ein Java-Objekt konvertiert werden kann.

Lassen Sie uns dies mit einem kurzen Beispiel verdeutlichen:

  • Der Client sendet eine GET-Anforderung an/foos, wobei der HeaderAccept aufapplication/json gesetzt ist, um alle Ressourcen vonFooals JSON abzurufen

  • DerFoo Spring Controller wird getroffen und gibt die entsprechendenFoo Java-Entitäten zurück

  • Spring verwendet dann einen der Jackson-Nachrichtenkonverter, um die Entitäten in JSON zu marshallen

Schauen wir uns nun die Besonderheiten an, wie dies funktioniert - und wie wir die Anmerkungen@ResponseBody und @RequestBody nutzen können.

3.2. @ResponseBody

@ResponseBody auf einer Controller-Methode zeigt Spring an, dassthe return value of the method is serialized directly to the body of the HTTP Response. Wie oben erläutert, wird der vom Client angegebene Header "Accept" verwendet, um den geeigneten HTTP-Konverter zum Marshalling der Entität auszuwählen.

Schauen wir uns ein einfaches Beispiel: an

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

Jetzt gibt der Client den Header "Accept" in der Anforderung fürapplication/json an - Beispielcurl Befehl:

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

Die KlasseFoo:

public class Foo {
    private long id;
    private String name;
}

Und der HTTP-Antworttext:

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

3.3. @RequestBody

Wir können die Annotation@RequestBody für das Argument einer Controller-Methode verwenden, umthat the body of the HTTP Request is deserialized to that particular Java entity anzugeben. Um den geeigneten Konverter zu bestimmen, verwendet Spring den "Content-Type" -Header aus der Client-Anfrage.

Schauen wir uns ein Beispiel an:

@PutMapping("/{id}")
public @ResponseBody void update(@RequestBody Foo foo, @PathVariable String id) {
    fooService.update(foo);
}

Als Nächstes verwenden wir dies mit einem JSON-Objekt. Wir geben "Content-Type alsapplication/json an:

curl -i -X PUT -H "Content-Type: application/json"
-d '{"id":"83","name":"klik"}' http://localhost:8080/spring-boot-rest/foos/1

Wir bekommen 200 OK zurück - eine erfolgreiche Antwort:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Fri, 10 Jan 2014 11:18:54 GMT

4. Konfiguration der benutzerdefinierten Konverter

Wir können auchcustomize the message converters by implementing the WebMvcConfigurer interface und die MethodeconfigureMessageConverters überschreiben:

@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;
    }
}


Und hier ist die entsprechende XML-Konfiguration:



    
        
        
            
            
        
    


In diesem Beispiel erstellen wir einen neuen Konverter -MarshallingHttpMessageConverter - und konfigurieren ihn mithilfe der Spring XStream-Unterstützung. Dies ermöglicht ein hohes Maß an Flexibilität, dawe’re working with the low-level APIs of the underlying marshalling framework - in diesem Fall XStream - und wir das konfigurieren können, wie wir wollen.

Beachten Sie, dass für dieses Beispiel die XStream-Bibliothek zum Klassenpfad hinzugefügt werden muss.

Beachten Sie auch, dass durch Erweitern dieser Unterstützungsklassewe’re losing the default message converters which were previously pre-registered.

Wir können jetzt natürlich dasselbe für Jackson tun - indem wir unsere eigenenMappingJackson2HttpMessageConverter. definieren. Wir können jetzt ein benutzerdefiniertesObjectMapper für diesen Konverter festlegen und es nach Bedarf konfigurieren lassen.

In diesem Fall war XStream die ausgewählte Marshaller / Unmarshaller-Implementierung, aberothers wieCastorMarshaller können ebenfalls verwendet werden.

Zu diesem Zeitpunkt - mit aktiviertem XML im Back-End - können wir die API mit XML-Repräsentationen verwenden:

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

4.1. Spring Boot-Unterstützung

Wenn wir Spring Boot verwenden, können wir vermeiden, denWebMvcConfigurer -Sand zu implementieren, indem alle Nachrichtenkonverter manuell hinzugefügt werden, wie oben beschrieben.

Wir können einfach verschiedeneHttpMessageConverter beans im Kontext definieren, und Spring Boot fügt sie automatisch der von ihm erstellten Autokonfiguration hinzu:

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

    // ...

    return xmlConverter;
}





5. Verwenden vonRestTemplatevon Spring mit HTTP-Nachrichtenkonvertern

Ebenso wie auf der Serverseite kann die HTTP-Nachrichtenkonvertierung auf der Clientseite auf den SpringRestTemplate konfiguriert werden.

Wir werden die Vorlage gegebenenfalls mit den Überschriften "Accept" und "Content-Type" konfigurieren. Dann werden wir versuchen, die REST-API mit vollständigem Marshalling und Unmarshalling derFoo-Ressource zu verwenden - sowohl mit JSON als auch mit XML.

5.1. Abrufen der Ressource ohne Header vonAccept

@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. Abrufen einer Ressource mitapplication/xml Accept Header

Lassen Sie uns die Ressource jetzt explizit als XML-Darstellung abrufen. Wir werden eine Reihe von Konvertern definieren und diese aufRestTemplate.etzen

Da wir XML verwenden, verwenden wir denselben XStream-Marshaller wie zuvor:

@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. Abrufen einer Ressource mitapplication/json Accept Header

In ähnlicher Weise verwenden wir jetzt die REST-API, indem wir nach JSON fragen:

@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. Aktualisieren Sie eine Ressource mit XMLContent-Type

Lassen Sie uns abschließend auch JSON-Daten an die REST-API senden und den Medientyp dieser Daten über den HeaderContent-Typeangeben:

@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());
}

Interessant ist hier, dass wir die Medientypen mischen können -we’re sending XML data but we’re waiting for JSON data back from the server. Dies zeigt, wie stark der Spring-Umwandlungsmechanismus wirklich ist.

6. Fazit

In diesem Tutorial haben wir uns angesehen, wie Spring MVC es uns ermöglicht, HTTP-Nachrichtenkonverter inautomatically marshall/unmarshall Java Entities to and from XML or JSON anzugeben und vollständig anzupassen. Dies ist natürlich eine vereinfachte Definition, und es gibt so viel mehr, was der Mechanismus zur Nachrichtenkonvertierung leisten kann - wie wir aus dem letzten Testbeispiel ersehen können.

Wir haben auch untersucht, wie derselbe leistungsstarke Mechanismus mit demRestTemplate-Client genutzt werden kann, was zu einer vollständig typsicheren Art der Verwendung der API führt.

Wie immer ist der in diesem Artikel vorgestellte Codeover on Github verfügbar.