ETags za REST s proljećem

OSTALO Vrh

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

Ovaj će se članak usredotočiti na rad s ETagovima u proljeće, integracijsko testiranje REST API-ja i scenariji potrošnje s kovrča.

2. OSTATAK i ETagovi

Iz službene proljetne dokumentacije o podršci za ETag:

ETag (oznaka entiteta) je zaglavlje HTTP odgovora koje vraća HTTP / 1.1 web-poslužitelj koji se koristi za određivanje promjene sadržaja na danom URL-u.

ETagove možemo koristiti za dvije stvari - predmemoriranje i uvjetne zahtjeve. The Vrijednost ETag može se smatrati raspršivanjem izračunato iz bajtova tijela odgovora. Budući da usluga vjerojatno koristi kriptografsku hash funkciju, čak i najmanja preinaka tijela drastično će promijeniti izlaz, a time i vrijednost ETag. To vrijedi samo za jake ETagove - protokol pruža i slabi Etag.

Korištenjem Ako-* zaglavlje pretvara standardni GET zahtjev u uvjetni GET. Dva Ako-* zaglavlja koja se koriste s ETagovima su "If-None-Match" i "If-Match" - svako sa svojom semantikom kako je objašnjeno kasnije u ovom članku.

3. Komunikacija klijent-poslužitelj s kovrča

Jednostavnu komunikaciju klijent-poslužitelj koja uključuje ETagove možemo podijeliti na korake:

Prvo, klijent uputi REST API poziv - Odgovor uključuje zaglavlje ETag koji će biti pohranjeni za daljnju upotrebu:

curl -H "Prihvati: application / json" -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "f88dd058fe004909615a64f01be66a7" Tip sadržaja: application / json; charset = UTF-8 Duljina sadržaja: 52

Za sljedeći zahtjev, Klijent će uključiti Ako se ne podudara zatražite zaglavlje s vrijednošću ETag iz prethodnog koraka. Ako se resurs nije promijenio na poslužitelju, odgovor neće sadržavati tijelo i statusni kod od 304 - Nije modificirano:

curl -H "Accept: application / json" -H 'If-None-Match: "f88dd058fe004909615a64f01be66a7"' -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 304 Nije izmijenjeno ETag: "f88dd058fe004909615a64f01be66a7"

Sada, prije ponovnog dohvaćanja resursa, promijenimo ga izvođenjem ažuriranja:

curl -H "Content-Type: application / json" -i -X ​​PUT --data '{"id": 1, "name": "Transformers2"}' // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "d41d8cd98f00b204e9800998ecf8427e" Duljina sadržaja: 0

Napokon, šaljemo posljednji zahtjev za ponovnim preuzimanjem Fooa. Imajte na umu da smo ga ažurirali od zadnjeg zahtjeva, pa prethodna vrijednost ETag više ne bi trebala raditi. Odgovor će sadržavati nove podatke i novi ETag koji se, opet, mogu pohraniti za daljnju upotrebu:

curl -H "Accept: application / json" -H 'If-None-Match: "f88dd058fe004909615a64f01be66a7"' -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "03cb37ca667706c68c0aad4cb04c3a211" Tip sadržaja: application / json; charset = UTF-8 Duljina sadržaja: 56

I tu ste, ETagovi u divljini i štede propusnost.

4. ETag podrška u proljeće

Na podršci za Spring: korištenje ETaga u Spring je izuzetno jednostavno za postavljanje i potpuno transparentno za aplikaciju. Podršku možemo omogućiti dodavanjem jednostavnog filtar u web.xml:

 etagFilter org.springframework.web.filter.ShallowEtagHeaderFilter etagFilter / foos / * 

Mapiramo filtar na isti URI obrazac kao i sam RESTful API. Sam filtar standardna je implementacija ETag funkcionalnosti od proljeća 3.0.

Provedba je plitka - aplikacija izračunava ETag na temelju odgovora, što će uštedjeti propusnost, ali ne i performanse poslužitelja.

Dakle, zahtjev koji će imati koristi od ETag podrške i dalje će se obrađivati ​​kao standardni zahtjev, konzumirati bilo koji resurs koji bi obično trošio (veze s bazom podataka itd.) I tek prije nego što se njegov odgovor vrati klijentu, ETag podrška će se pokrenuti u.

Tada će se ETag izračunati iz tijela odgovora i postaviti na sam resurs; također, ako je Ako se ne podudara zaglavlje je postavljeno na Zahtjev, i njime će se rukovati.

Dublja primjena ETag mehanizma mogla bi potencijalno pružiti puno veće koristi - poput služenja nekim zahtjevima iz predmemorije i uopće ne izvršavanja izračuna - ali implementacija sasvim sigurno ne bi bila tako jednostavna, niti tako prilagodljiva kao plitki pristup ovdje opisano.

4.1. Konfiguracija temeljena na Javi

Pogledajmo kako bi izgledala Java konfiguracija proglašavajući a ShallowEtagHeaderFilter grah u našem proljetnom kontekstu:

@Bean public ShallowEtagHeaderFilter shallowEtagHeaderFilter () {return new ShallowEtagHeaderFilter (); }

Imajte na umu da ako trebamo pružiti daljnje konfiguracije filtra, možemo umjesto toga proglasiti a FilterRegistrationBean primjer:

@Bean javni FilterRegistrationBean shallowEtagHeaderFilter () {FilterRegistrationBean filterRegistrationBean = novi FilterRegistrationBean (novi ShallowEtagHeaderFilter ()); filterRegistrationBean.addUrlPatterns ("/ foos / *"); filterRegistrationBean.setName ("etagFilter"); povratni filterRegistrationBean; }

Napokon, ako ne koristimo Spring Boot, filtar možemo postaviti pomoću AbstractAnnotationConfigDispatcherServletInitializer‘S getServletFilters metoda.

4.2. Korištenje ResponseEntity's eTag () Metoda

Ova je metoda predstavljena u proljetnom okviru 4.1 možemo ga koristiti za kontrolu vrijednosti ETag koju pojedinačna krajnja točka dohvaća.

Na primjer, zamislimo da verzijske entitete koristimo kao mehanizam zaključavanja optimista za pristup podacima iz naše baze podataka.

Možemo koristiti samu verziju kao ETag da naznačimo je li entitet modificiran:

@GetMapping (value = "/ {id} / custom-etag") javni ResponseEntity findByIdWithCustomEtag (@PathVariable ("id") final Long id) {// ... Foo foo = ... return ResponseEntity.ok (). eTag (Long.toString (foo.getVersion ())) .body (foo); }

Usluga će dohvatiti odgovarajuću 304-Nije modificirano Navedite odgovara li uslovno zaglavlje zahtjeva predmemoriranim podacima.

5. Testiranje ETagova

Krenimo jednostavno - moramo provjeriti da li će odgovor jednostavnog zahtjeva za preuzimanjem jednog resursa zapravo vratiti "ETag " Zaglavlje:

@Test javna praznina givenResourceExists_whenRetrievingResource_thenEtagIsAlsoReturned () {// Dat je niz uriOfResource = createAsUri (); // When Response findOneResponse = RestAssured.given (). zaglavlje ("Prihvati", "application / json"). get (uriOfResource); // Zatim assertNotNull (findOneResponse.getHeader ("ETag")); }

Sljedeći, provjeravamo sretan put ETag ponašanja. Ako je Zahtjev za dohvaćanje Resurs s poslužitelja koristi ispravnu ETag vrijednost, tada poslužitelj ne dohvaća resurs:

@Test javna praznina givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned () {// Dat je niz uriOfResource = createAsUri (); Response findOneResponse = RestAssured.given (). zaglavlje ("Prihvati", "application / json"). get (uriOfResource); Niz etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); // When Response secondFindOneResponse = RestAssured.given (). header ("Accept", "application / json"). headers ("If-None-Match", etagValue) .get (uriOfResource); // Zatim assertTrue (secondFindOneResponse.getStatusCode () == 304); }

Korak po korak:

  • stvaramo i dohvaćamo resurs, spremanje the ETag vrijednost
  • poslati novi zahtjev za preuzimanje, ovaj put s "Ako se ne podudara"Zaglavlje koje specificira ETag prethodno pohranjena vrijednost
  • na ovaj drugi zahtjev, poslužitelj jednostavno vraća a 304 Nije izmijenjeno, budući da se sam resurs doista nije promijenio između dviju operacija pronalaženja

Konačno, provjeravamo slučaj kada se Resurs mijenja između prvog i drugog zahtjeva za preuzimanje:

@Test javna praznina givenResourceWasRetrievedThenModified_whenRetrievingAgainWithEtag_thenResourceIsReturned () {// Dat je niz uriOfResource = createAsUri (); Response findOneResponse = RestAssured.given (). zaglavlje ("Prihvati", "application / json"). get (uriOfResource); Niz etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); postojećiResource.setName (randomAlphabetic (6)); ažuriranje (postojećiResource); // When Response secondFindOneResponse = RestAssured.given (). header ("Accept", "application / json"). headers ("If-None-Match", etagValue) .get (uriOfResource); // Zatim assertTrue (secondFindOneResponse.getStatusCode () == 200); }

Korak po korak:

  • prvo stvorimo i dohvatimo a Resurs - i spremite ETag vrijednost za daljnju upotrebu
  • onda isto ažuriramo Resurs
  • poslati novi GET zahtjev, ovaj put s “Ako se ne podudara"Zaglavlje koje specificira ETag koje smo prethodno pohranili
  • na ovaj drugi zahtjev, poslužitelj će vratiti a 200 OK zajedno s punim Resursom, od ETag vrijednost više nije točna, jer smo u međuvremenu ažurirali Resurs

Konačno, posljednji test - koji neće uspjeti jer funkcionalnost još nije implementirana na proljeće - jest podrška za Ako se podudara HTTP zaglavlje:

@Test javna praznina givenResourceExists_whenRetrievedWithIfMatchIncorrectEtag_then412IsReceived () {// S obzirom na T postojeći resurs = getApi (). Create (createNewEntity ()); // When String uriOfResource = baseUri + "/" + existingResource.getId (); Response findOneResponse = RestAssured.given (). Zaglavlje ("Prihvati", "application / json"). zaglavlja ("If-Match", randomAlphabetic (8)). get (uriOfResource); // Zatim assertTrue (findOneResponse.getStatusCode () == 412); }

Korak po korak:

  • stvaramo Resurs
  • zatim ga dohvatite pomoću "Ako se podudara”Zaglavlje koje navodi netočno ETag vrijednost - ovo je uvjetni GET zahtjev
  • poslužitelj bi trebao vratiti a 412 Preduvjet nije uspio

6. ETagovi su veliki

Za operacije čitanja koristili smo samo ETagove. Postoji RFC koji pokušava pojasniti kako bi se implementacije trebale nositi s ETagovima na operacijama pisanja - ovo nije standardno, ali je zanimljivo štivo.

Postoje, naravno, i druge moguće primjene ETag mehanizma, poput optimističnog mehanizma zaključavanja, kao i rješavanje povezanog problema „Izgubljeno ažuriranje“.

Postoji i nekoliko poznatih potencijalnih zamki i upozorenja kojih morate biti svjesni kada upotrebljavate ETagove.

7. Zaključak

Ovaj je članak samo ogrebao površinu onoga što je moguće s Springom i ETagovima.

Za potpunu implementaciju usluge RESTful s omogućenim ETagom, zajedno s integracijskim testovima koji provjeravaju ponašanje ETaga, pogledajte projekt GitHub.

OSTALO dno

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ