Http-meddelelsesomformere med Spring Framework

1. Oversigt

Denne artikel beskriver hvordan man konfigurerer HttpMessageConverters om foråret.

Kort sagt kan vi bruge beskedkonvertere til at marshallere og unmarshall Java-objekter til og fra JSON, XML osv. - via HTTP.

2. Grundlæggende

2.1. Aktivér web-MVC

Til at begynde med skal webapplikationen være konfigureret med Spring MVC support. En bekvem og meget tilpasselig måde at gøre dette på er at bruge @EnableWebMvc kommentar:

@EnableWebMvc @ Configuration @ ComponentScan ({"com.baeldung.web"}) offentlig klasse WebConfig implementerer WebMvcConfigurer {...}

Bemærk, at denne klasse implementerer WebMvcConfigurer - som giver os mulighed for at ændre standardlisten over Http-konvertere med vores egne.

2.2. Standardkonverteringsmeddelelsen

Som standard er følgende HttpMessageConverter s forekomster er forudaktiveret:

  • ByteArrayHttpMessageConverter - konverterer byte-arrays
  • StringHttpMessageConverter - konverterer strenge
  • ResourceHttpMessageConverter - konverterer org.springframework.core.io.Ressource til enhver form for oktetstrøm
  • SourceHttpMessageConverter - konverterer javax.xml.transform.Source
  • FormHttpMessageConverter - konverterer formdata til / fra a MultiValueMap.
  • Jaxb2RootElementHttpMessageConverter - konverterer Java-objekter til / fra XML (tilføjes kun hvis JAXB2 er til stede på klassestien)
  • MappingJackson2HttpMessageConverter - konverterer JSON (tilføjes kun hvis Jackson 2 er til stede på klassestien)

  • MappingJacksonHttpMessageConverter - konverterer JSON (tilføjes kun hvis Jackson er til stede på klassestien)
  • AtomFeedHttpMessageConverter - konverterer Atom-feeds (tilføjes kun hvis Rom er til stede på klassestien)
  • RssChannelHttpMessageConverter - konverterer RSS-feeds (tilføjes kun hvis Rom er til stede på klassestien)

3. Klient-server kommunikation - kun JSON

3.1. Forhandling på højt niveau

Hver HttpMessageConverter implementering har en eller flere tilknyttede MIME-typer.

Når du modtager en ny anmodning, Foråret vil bruge “Acceptere”-Overskrift for at bestemme medietypen, som den skal reagere med.

Det forsøger derefter at finde en registreret konverter, der er i stand til at håndtere den specifikke medietype. Endelig bruger den dette til at konvertere enheden og sende svaret tilbage.

Processen er den samme for at modtage en anmodning, der indeholder JSON-oplysninger. Rammen vil brug "Indholdstype”-Overskrift for at bestemme medietypen på anmodningsorganet.

Derefter søger den efter en HttpMessageConverter der kan konvertere kroppen sendt af klienten til et Java-objekt.

Lad os præcisere dette med et hurtigt eksempel:

  • klienten sender en GET-anmodning til / foos med Acceptere header indstillet til ansøgning / json - for at få alle Foo ressourcer som JSON
  • det Foo Spring Controller er ramt og returnerer det tilsvarende Foo Java-enheder
  • Spring bruger derefter en af ​​Jackson-meddelelsesomformerne til at samle enhederne til JSON

Lad os nu se på detaljerne i, hvordan dette fungerer - og hvordan vi kan udnytte @ResponseBody og @RequestBody kommentarer.

3.2. @ResponseBody

@ResponseBody på en Controller-metode angiver det til Spring metodeens returværdi serialiseres direkte til selve HTTP-svaret. Som diskuteret ovenfor erAcceptere”Header specificeret af klienten vil blive brugt til at vælge den passende Http Converter til at samle enheden.

Lad os se på et simpelt eksempel:

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

Nu vil klienten specificere "Accepter" overskriften til ansøgning / json i anmodningen - eksempel krølle kommando:

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

Det Foo klasse:

offentlig klasse Foo {privat lang id; privat strengnavn; }

Og Http-responsorganet:

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

3.3. @RequestBody

Vi kan bruge @RequestBody kommentar om argumentet for en controller-metode, der skal angives at selve HTTP-anmodningen deserialiseres til den pågældende Java-enhed. For at bestemme den passende konverter bruger Spring "Content-Type" -overskriften fra klientanmodningen.

Lad os se på et eksempel:

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

Lad os derefter forbruge dette med et JSON-objekt - vi specificerer “Content-Type at være ansøgning / json:

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

Vi får 200 OK tilbage - et vellykket svar:

HTTP / 1.1 200 OK Server: Apache-Coyote / 1.1 Indholdslængde: 0 Dato: Fre, 10. januar 2014 11:18:54 GMT

4. Konfiguration af brugerdefinerede konvertere

Det kan vi også tilpasse meddelelseskonvertere ved at implementere WebMvcConfigurer interface og tilsidesættelse af configureMessageConverters metode:

@EnableWebMvc @Configuration @ComponentScan ({"com.baeldung.web"}) public class WebConfig implementerer WebMvcConfigurer {@Override public void configureMessageConverters (List konvertere) {messageConverters.add (createXmlHttpMessageConverter ()); messageConverters.add (ny MappingJackson2HttpMessageConverter ()); } private HttpMessageConverter createXmlHttpMessageConverter () {MarshallingHttpMessageConverter xmlConverter = ny MarshallingHttpMessageConverter (); XStreamMarshaller xstreamMarshaller = ny XStreamMarshaller (); xmlConverter.setMarshaller (xstreamMarshaller); xmlConverter.setUnmarshaller (xstreamMarshaller); returnere xmlConverter; }}

I dette eksempel opretter vi en ny konverter - MarshallingHttpMessageConverter - og ved hjælp af Spring XStream support til at konfigurere det. Dette giver stor fleksibilitet siden Vi arbejder med API'er på lavt niveau i den underliggende marshalling-ramme - i dette tilfælde XStream - og vi kan konfigurere det, men vi vil.

Bemærk, at dette eksempel kræver tilføjelse af XStream-biblioteket til klassestien.

Vær også opmærksom på, at ved at udvide denne supportklasse, vi mister standardmeddelelseskonvertere, som tidligere var præregistreret.

Vi kan naturligvis nu gøre det samme for Jackson - ved at definere vores egne MappingJackson2HttpMessageConverter. Vi kan nu indstille en brugerdefineret ObjectMapper på denne konverter og få den konfigureret, som vi har brug for.

I dette tilfælde var XStream den valgte marshaller / unmarshaller implementering, men andre kan lide CastorMarshaller kan også bruges.

På dette tidspunkt - med XML aktiveret på bagsiden - kan vi forbruge API'en med XML-repræsentationer:

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

4.1. Spring Boot Support

Hvis vi bruger Spring Boot, kan vi undgå at implementere WebMvcConfigurer og tilføje alle Message Converters manuelt som vi gjorde ovenfor.

Vi kan bare definere forskellige HttpMessageConverter bønner i sammenhængen, og Spring Boot tilføjer dem automatisk til den autokonfiguration, den opretter:

@Bean public HttpMessageConverter createXmlHttpMessageConverter () {MarshallingHttpMessageConverter xmlConverter = ny MarshallingHttpMessageConverter (); // ... return xmlConverter; }

5. Brug af forårets RestTemplate Med Http-meddelelsesomformere

Ud over serversiden kan Http Message Conversion konfigureres på klientsiden på foråret RestTemplate.

Vi konfigurerer skabelonen med “Acceptere”Og“Indholdstype”Overskrifter, når det er relevant. Så prøver vi at forbruge REST API med fuld marshalling og unmarshalling af Foo Ressource - både med JSON og med XML.

5.1. Hentning af ressourcen med nr Acceptere Header

@Test offentlig ugyldighed testGetFoo () {String URI = “// localhost: 8080 / spring-boot-rest / foos / {id}"; RestTemplate restTemplate = new RestTemplate (); Foo foo = restTemplate.getForObject (URI, Foo. klasse, "1"); Assert.assertEquals (nyt heltal (1), foo.getId ());}

5.2. Henter en ressource med applikation / xml Accepter header

Lad os nu eksplicit hente ressourcen som en XML-repræsentation. Vi skal definere et sæt konvertere og sætte dem på RestTemplate.

Fordi vi bruger XML, skal vi bruge den samme XStream marshaller som før:

@Test offentlig ugyldighed givenConsumingXml_whenReadingTheFoo_thenCorrect () {String URI = BASE_URI + "foos / {id}"; RestTemplate restTemplate = ny RestTemplate (); restTemplate.setMessageConverters (getMessageConverters ()); HttpHeaders headers = nye HttpHeaders (); headers.setAccept (Arrays.asList (MediaType.APPLICATION_XML)); HttpEntity-enhed = ny HttpEntity (overskrifter); ResponseEntity respons = restTemplate.exchange (URI, HttpMethod.GET, enhed, Foo.class, "1"); Foo ressource = respons.getBody (); assertThat (resource, notNullValue ()); } privat liste getMessageConverters () {XStreamMarshaller marshaller = ny XStreamMarshaller (); MarshallingHttpMessageConverter marshallingConverter = ny MarshallingHttpMessageConverter (marshaller); Liste konvertere = ArrayList(); converters.add (marshallingConverter); returomformere; }

5.3. Henter en ressource med ansøgning / json Accepter header

Lad os nu også forbruge REST API ved at bede om JSON:

@Test offentligt ugyldigt givetConsumingJson_whenReadingTheFoo_thenCorrect () {String URI = BASE_URI + "foos / {id}"; RestTemplate restTemplate = ny RestTemplate (); restTemplate.setMessageConverters (getMessageConverters ()); HttpHeaders headers = nye HttpHeaders (); headers.setAccept (Arrays.asList (MediaType.APPLICATION_JSON)); HttpEntity-enhed = ny HttpEntity (overskrifter); ResponseEntity respons = restTemplate.exchange (URI, HttpMethod.GET, enhed, Foo.class, "1"); Foo ressource = respons.getBody (); assertThat (resource, notNullValue ()); } privat liste getMessageConverters () {Liste konvertere = ny ArrayList(); converters.add (ny MappingJackson2HttpMessageConverter ()); returomformere; }

5.4. Opdater en ressource med XML Indholdstype

Endelig lad os også sende JSON-data til REST API og specificere medietypen for disse data via Indholdstype header:

@Test offentlig ugyldighed givenConsumingXml_whenWritingTheFoo_thenCorrect () {String URI = BASE_URI + "foos / {id}"; RestTemplate restTemplate = ny RestTemplate (); restTemplate.setMessageConverters (getMessageConverters ()); Foo ressource = ny Foo (4, "jason"); HttpHeaders headers = nye HttpHeaders (); headers.setAccept (Arrays.asList (MediaType.APPLICATION_JSON)); headers.setContentType ((MediaType.APPLICATION_XML)); HttpEntity-enhed = ny HttpEntity (ressource, overskrifter); ResponseEntity respons = restTemplate.exchange (URI, HttpMethod.PUT, enhed, Foo.class, resource.getId ()); Foo fooResponse = respons.getBody (); Assert.assertEquals (resource.getId (), fooResponse.getId ()); }

Hvad der er interessant her er, at vi er i stand til at blande medietyperne - vi sender XML-data, men vi venter på JSON-data tilbage fra serveren. Dette viser, hvor kraftig Springkonverteringsmekanismen virkelig er.

6. Konklusion

I denne vejledning så vi på, hvordan Spring MVC giver os mulighed for at specificere og tilpasse Http Message Converters fuldt ud til automatisk marshall / unmarshall Java-enheder til og fra XML eller JSON. Dette er selvfølgelig en forenklet definition, og der er så meget mere, som meddelelseskonverteringsmekanismen kan gøre - som vi kan se fra det sidste testeksempel.

Vi har også set på, hvordan man kan udnytte den samme kraftfulde mekanisme med RestTemplate klient - hvilket fører til en fuldt typesikker måde at forbruge API'en på.

Som altid er koden præsenteret i denne artikel tilgængelig på Github.