HATEOAS pre jarnú REST službu

ODPOČINOK Najlepšie

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

1. Prehľad

Tento článok sa zameria na: implementácia objaviteľnosti v jarnej službe REST a o splnení obmedzenia HATEOAS.

Tento článok sa zameriava na jarné MVC. Náš článok Úvod do jarných HATEOAS popisuje, ako používať HATEOAS v Spring Boot.

2. Oddelenie viditeľnosti prostredníctvom udalostí

Viditeľnosť ako samostatný aspekt alebo starosť webovej vrstvy by sa mala oddeliť od radiča spracovanie požiadavky HTTP. Za týmto účelom kontrolór vypáli udalosti pre všetky akcie, ktoré si vyžadujú ďalšiu manipuláciu s odpoveďou.

Najskôr vytvorme udalosti:

verejná trieda SingleResourceRetrieved rozširuje ApplicationEvent {súkromná odpoveď HttpServletResponse; public SingleResourceRetrieved (zdroj objektu, odpoveď HttpServletResponse) {super (zdroj); this.response = odpoveď; } public HttpServletResponse getResponse () {návratová odpoveď; }} verejná trieda ResourceCreated rozširuje ApplicationEvent {súkromná odpoveď HttpServletResponse; súkromný dlhý idOfNewResource; public ResourceCreated (Object source, HttpServletResponse response, long idOfNewResource) {super (source); this.response = odpoveď; this.idOfNewResource = idOfNewResource; } public HttpServletResponse getResponse () {návratová odpoveď; } public long getIdOfNewResource () {return idOfNewResource; }}

Potom, ovládač, s 2 jednoduchými operáciami - nájsť podľa id a vytvoriť:

@RestController @RequestMapping (value = "/ foos") verejná trieda FooController {@Autowired private ApplicationEventPublisher eventPublisher; @Autowired private IFooService service; @GetMapping (value = "foos / {id}") public Foo findById (@PathVariable ("id") Long id, HttpServletResponse response) {Foo resourceById = Preconditions.checkNotNull (service.findOne (id)); eventPublisher.publishEvent (nový SingleResourceRetrieved (toto, odpoveď)); vrátiť resourceById; } @PostMapping @ResponseStatus (HttpStatus.CREATED) public void create (prostriedok @RequestBody Foo, odpoveď HttpServletResponse) {Preconditions.checkNotNull (zdroj); Dlhé newId = service.create (zdroj) .getId (); eventPublisher.publishEvent (nový ResourceCreated (this, response, newId)); }}

Potom môžeme tieto udalosti zvládnuť s ľubovoľným počtom odpojených poslucháčov. Každý z nich sa môže zamerať na svoj konkrétny prípad a pomôcť tak uspokojiť celkové obmedzenie HATEOAS.

Poslucháči by mali byť poslednými objektmi v zásobníku hovorov a nie je k nim potrebný žiadny priamy prístup; ako také nie sú verejné.

3. Sprístupnenie identifikátora URI novovytvoreného zdroja

Ako už bolo spomenuté v predchádzajúcom príspevku na serveri HATEOAS, operácia vytvorenia nového zdroja by mala vrátiť URI tohto zdroja v Poloha Hlavička HTTP odpovede.

Riešime to pomocou poslucháča:

@Component class ResourceCreatedDiscoverabilityListener implementuje ApplicationListener {@Override public void onApplicationEvent (ResourceCreated resourceCreatedEvent) {Preconditions.checkNotNull (resourceCreatedEvent); HttpServletResponse response = resourceCreatedEvent.getResponse (); dlhý idOfNewResource = resourceCreatedEvent.getIdOfNewResource (); addLinkHeaderOnResourceCreation (odpoveď, idOfNewResource); } void addLinkHeaderOnResourceCreation (odpoveď HttpServletResponse, dlhé idOfNewResource) {URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri (). cesta ("/ {idOfNewResource}"). buildAndExpand (idOfNewResource) .toUri (); response.setHeader ("Umiestnenie", uri.toASCIIString ()); }}

V tomto príklade využívame ServletUriComponentsBuilder - ktorý pomáha pri používaní aktuálnej požiadavky. Takto nemusíme nič obchádzať a môžeme k tomu jednoducho pristupovať staticky.

Ak by sa API vrátilo ResponseEntity - mohli by sme použiť aj Poloha podpora.

4. Získanie jediného zdroja

Pri načítaní jedného zdroja klient by mal byť schopný objaviť URI, aby získal všetky zdroje tohto typu:

@Component triedy SingleResourceRetrievedDiscoverabilityListener implementuje ApplicationListener {@Override public void onApplicationEvent (SingleResourceRetrieved resourceRetrievedEvent) {Preconditions.checkNotNull (resourceRetrievedEvent); HttpServletResponse response = resourceRetrievedEvent.getResponse (); addLinkHeaderOnSingleResourceRetrieval (požiadavka, odpoveď); } void addLinkHeaderOnSingleResourceRetrieval (odpoveď HttpServletResponse) {String requestURL = ServletUriComponentsBuilder.fromCurrentRequestUri (). build (). toUri (). toASCIIString (); int positionOfLastSlash = requestURL.lastIndexOf ("/"); Reťazec uriForResourceCreation = requestURL.substring (0, positionOfLastSlash); Reťazec linkHeaderValue = LinkUtil .createLinkHeader (uriForResourceCreation, "zbierka"); response.addHeader (LINK_HEADER, linkHeaderValue); }}

Všimnite si, že sémantika vzťahovej väzby využíva „Zbierka“ typ vzťahu, špecifikovaný a používaný v niekoľkých mikroformátoch, ale zatiaľ nie je štandardizovaný.

The Odkaz hlavička je jednou z najpoužívanejších hlavičiek HTTPna účely zistiteľnosti. Pomôcka na vytvorenie tejto hlavičky je dosť jednoduchá:

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

5. Viditeľnosť v koreni

Koreň je vstupným bodom v celej službe - je to to, s čím klient prichádza do styku pri prvej konzumácii API.

Ak sa má zohľadniť a implementovať obmedzenie HATEOAS, potom je na mieste začať. Preto všetky hlavné URI systému musia byť viditeľné z koreňového adresára.

Pozrime sa teraz na kontrolór:

@GetMapping ("/") @ResponseStatus (hodnota = HttpStatus.NO_CONTENT) public void adminRoot (konečná požiadavka HttpServletRequest, konečná odpoveď HttpServletResponse) {String rootUri = request.getRequestURL (). ToString (); URI fooUri = nový UriTemplate ("{rootUri} {zdroj}"). Expand (rootUri, "foos"); Reťazec linkToFoos = LinkUtil.createLinkHeader (fooUri.toASCIIString (), "kolekcia"); response.addHeader ("Odkaz", linkToFoos); }

Toto je samozrejme ilustrácia konceptu zameraného na jediný URI vzorky pre Foo Zdroje. Skutočná implementácia by mala podobne pridať identifikátory URI pre všetky zdroje zverejnené klientovi.

5.1. Viditeľnosť nie je o zmene URI

Môže to byť kontroverzný bod - na jednej strane účelom HATEOAS je, aby klient objavil URI API a nespoliehal sa na pevne zakódované hodnoty. Na druhej strane - takto web nefunguje: áno, identifikátory URI sú objavené, ale sú tiež označené ako záložky.

Jemným, ale dôležitým rozdielom je vývoj API - staré URI by mali stále fungovať, ale každý klient, ktorý objaví API, by mal objaviť nové URI - čo umožňuje dynamicky sa meniť API a dobrým klientom dobre fungovať, aj keď Zmeny API.

Na záver - len preto, že všetky URI webovej služby RESTful by sa mali považovať za super URI (a super URI sa nemenia) - to neznamená, že dodržiavanie obmedzenia HATEOAS nie je pri vývoji API mimoriadne užitočné.

6. Upozornenia na objaviteľnosť

Ako sa uvádza v niektorých diskusiách okolo predchádzajúcich článkov, prvým cieľom objaviteľnosti je minimálne alebo žiadne použitie dokumentácie a umožniť klientovi naučiť sa a rozumieť tomu, ako používať API, prostredníctvom odpovedí, ktoré dostane.

Toto by sa v skutočnosti nemalo považovať za taký priťahovaný ideál - je to spôsob, akým spotrebúvame každú novú webovú stránku - bez akejkoľvek dokumentácie. Pokiaľ je teda koncepcia v kontexte REST problematickejšia, musí ísť o technickú implementáciu, nie o otázku, či je alebo nie je to možné.

Z technického hľadiska však ešte stále nemáme ďaleko od plne funkčného riešenia - špecifikácia a podpora rámca sa stále vyvíjajú, a preto musíme urobiť určité kompromisy.

7. Záver

Tento článok sa venoval implementácii niektorých znakov objaviteľnosti v kontexte služby RESTful Service s jarným MVC a dotkol sa koncepcie objaviteľnosti v koreňovom adresári.

Implementáciu všetkých týchto príkladov a útržkov kódu nájdete na serveri GitHub - jedná sa o projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.

REST spodok

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

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