HATEOAS for en REST-tjeneste for våren

REST Topp

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

Denne artikkelen vil fokusere på implementering av oppdagbarhet i en Spring REST-tjeneste og på å tilfredsstille HATEOAS-begrensningen.

Denne artikkelen fokuserer på Spring MVC. Vår artikkel An Intro to Spring HATEOAS beskriver hvordan du bruker HATEOAS i Spring Boot.

2. Koble fra oppdagbarhet gjennom hendelser

Oppdagbarhet som et eget aspekt eller bekymring for weblaget bør kobles fra kontrolleren håndtere HTTP-forespørselen. For dette formålet vil kontrolleren skyte av hendelser for alle handlingene som krever ytterligere manipulering av responsen.

La oss først lage hendelsene:

offentlig klasse SingleResourceRetrieved utvider ApplicationEvent {private HttpServletResponse svar; offentlig SingleResourceRetrieved (Objektkilde, HttpServletResponse-svar) {super (kilde); this.response = respons; } offentlig HttpServletResponse getResponse () {returrespons; }} offentlig klasse ResourceCreated utvider ApplicationEvent {private HttpServletResponse svar; privat lang idOfNewResource; public ResourceCreated (Objektkilde, HttpServletResponse-svar, lang idOfNewResource) {super (kilde); this.response = respons; this.idOfNewResource = idOfNewResource; } offentlig HttpServletResponse getResponse () {returrespons; } offentlig lang getIdOfNewResource () {return idOfNewResource; }}

Deretter, kontrolleren, med to enkle operasjoner - finn etter id og skape:

@RestController @RequestMapping (verdi = "/ foos") offentlig klasse FooController {@Autowired privat ApplicationEventPublisher eventPublisher; @Autowired privat IFooService-tjeneste; @GetMapping (value = "foos / {id}") offentlig Foo findById (@PathVariable ("id") Lang id, HttpServletResponse-svar) {Foo resourceById = Preconditions.checkNotNull (service.findOne (id)); eventPublisher.publishEvent (ny SingleResourceRetrieved (dette, svar)); return resourceById; } @PostMapping @ResponseStatus (HttpStatus.CREATED) public void create (@RequestBody Foo resource, HttpServletResponse response) {Preconditions.checkNotNull (resource); Lang newId = service.create (ressurs) .getId (); eventPublisher.publishEvent (new ResourceCreated (this, response, newId)); }}

Vi kan deretter håndtere disse hendelsene med et hvilket som helst antall frakoblede lyttere. Hver av disse kan fokusere på sin egen spesielle sak og bidra til å tilfredsstille den generelle HATEOAS-begrensningen.

Lytterne skal være de siste objektene i samtalestakken, og det er ikke nødvendig med direkte tilgang til dem. som sådan er de ikke offentlige.

3. Gjøre URI av en nyopprettet ressurs oppdagelig

Som diskutert i forrige innlegg på HATEOAS, operasjonen med å opprette en ny ressurs skal returnere URI for den ressursen i plassering HTTP-overskrift av svaret.

Vi takler dette ved hjelp av en lytter:

@Komponent klasse ResourceCreatedDiscoverabilityListener implementerer ApplicationListener {@Override public void onApplicationEvent (ResourceCreated resourceCreatedEvent) {Preconditions.checkNotNull (resourceCreatedEvent); HttpServletResponse respons = resourceCreatedEvent.getResponse (); lang idOfNewResource = resourceCreatedEvent.getIdOfNewResource (); addLinkHeaderOnResourceCreation (respons, idOfNewResource); } ugyldig addLinkHeaderOnResourceCreation (HttpServletResponse respons, lang idOfNewResource) {URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri (). sti ("/ {idOfNewResource}"). buildAndExpand (idOfNewResource) .toUri (); response.setHeader ("Location", uri.toASCIIString ()); }}

I dette eksemplet, vi bruker ServletUriComponentsBuilder - som hjelper med å bruke gjeldende forespørsel. På denne måten trenger vi ikke å gi noe rundt, og vi kan bare få tilgang til dette statisk.

Hvis API-en ville komme tilbake ResponseEntity - vi kunne også bruke plassering Brukerstøtte.

4. Få en enkelt ressurs

Når du henter en enkelt ressurs, klienten skal kunne oppdage URI for å få alle ressurser av den typen:

@Komponentklasse SingleResourceRetrievedDiscoverabilityListener implementerer ApplicationListener {@Override public void onApplicationEvent (SingleResourceRetrieved resourceRetrievedEvent) {Preconditions.checkNotNull (resourceRetrievedEvent); HttpServletResponse respons = resourceRetrievedEvent.getResponse (); addLinkHeaderOnSingleResourceRetrieval (forespørsel, svar); } ugyldig addLinkHeaderOnSingleResourceRetrieval (HttpServletResponse respons) {StrengforespørselURL = ServletUriComponentsBuilder.fromCurrentRequestUri (). build (). toUri (). toASCIIString (); int positionOfLastSlash = requestURL.lastIndexOf ("/"); String uriForResourceCreation = requestURL.substring (0, positionOfLastSlash); Streng linkHeaderValue = LinkUtil .createLinkHeader (uriForResourceCreation, "samling"); response.addHeader (LINK_HEADER, linkHeaderValue); }}

Merk at semantikken i koblingsforholdet bruker "samling" relasjonstype, spesifisert og brukt i flere mikroformater, men ennå ikke standardisert.

De Link header er en av de mest brukte HTTP-overskriftenefor å oppdage. Verktøyet for å lage denne overskriften er enkelt nok:

public class LinkUtil {public static String createLinkHeader (String uri, String rel) {return "; rel = \" "+ rel +" \ ""; }}

5. Oppdagbarhet ved roten

Roten er inngangspunktet i hele tjenesten - det er det klienten kommer i kontakt med når han bruker API for første gang.

Hvis HATEOAS-begrensningen skal vurderes og implementeres gjennomgående, er dette stedet å starte. Derfor alle de viktigste URI-ene i systemet må kunne oppdages fra roten.

La oss nå se på kontrolleren for dette:

@GetMapping ("/") @ResponseStatus (verdi = HttpStatus.NO_CONTENT) offentlig ugyldig adminRoot (endelig HttpServletRequest-forespørsel, endelig HttpServletResponse-svar) {String rootUri = request.getRequestURL (). ToString (); URI fooUri = ny UriTemplate ("{rootUri} {resource}"). Utvid (rootUri, "foos"); String linkToFoos = LinkUtil.createLinkHeader (fooUri.toASCIIString (), "samling"); response.addHeader ("Link", linkToFoos); }

Dette er selvfølgelig en illustrasjon av konseptet, med fokus på en enkelt URI-prøve, for Foo Ressurser. En reell implementering bør på samme måte legge til URI for alle ressursene som er publisert til klienten.

5.1. Oppdagbarhet handler ikke om å endre URI-er

Dette kan være et kontroversielt poeng - på den ene siden er formålet med HATEOAS å få klienten til å oppdage URI-ene til API-en og ikke stole på hardkodede verdier. På den annen side - dette er ikke hvordan nettet fungerer: ja, URI-er blir oppdaget, men de er også bokmerket.

Et subtilt, men viktig skille er utviklingen av API-en - de gamle URI-ene skal fortsatt fungere, men enhver klient som vil oppdage API-en, bør oppdage de nye URI-ene - som lar API-en endres dynamisk, og gode klienter kan fungere bra selv når API-endringer.

Avslutningsvis - bare fordi alle URI-er i RESTful-nettjenesten skal betraktes som kule URI-er (og kule URI-er endres ikke) - betyr ikke det at det å overholde HATEOAS-begrensningen ikke er ekstremt nyttig når du utvikler API.

6. forbehold om oppdagbarhet

Som det fremgår av noen av diskusjonene rundt de forrige artiklene, det første målet med oppdagbarhet er å bruke minimal eller ingen bruk av dokumentasjon og få klienten til å lære og forstå hvordan man bruker API-en via svarene den får.

Dette skal faktisk ikke betraktes som et så langt hentet ideal - det er slik vi bruker hver nye webside - uten dokumentasjon. Så hvis konseptet er mer problematisk i sammenheng med REST, så må det dreie seg om teknisk implementering, ikke om et spørsmål om det er mulig eller ikke.

Når det er sagt, er vi teknisk sett fortsatt langt fra en fullt fungerende løsning - spesifikasjonen og rammestøtten er fortsatt i utvikling, og på grunn av det må vi inngå kompromisser.

7. Konklusjon

Denne artikkelen dekket implementeringen av noen av egenskapene til oppdagbarhet i sammenheng med en RESTful Service med Spring MVC og berørte begrepet oppdagbarhet ved roten.

Implementeringen av alle disse eksemplene og kodebitene finner du på GitHub - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.

HVILLE bunnen

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

$config[zx-auto] not found$config[zx-overlay] not found