ETags for REST tavasszal

REST felső

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

1. Áttekintés

Ez a cikk a következőkre fog összpontosítani tavasszal az ETags-szal dolgozik, a REST API és a fogyasztási forgatókönyvek integrációs tesztelése becsavar.

2. REST és ETags

Az ETag támogatás hivatalos tavaszi dokumentációjából:

Az ETag (entitás címke) egy HTTP válasz fejléc, amelyet egy HTTP / 1.1 kompatibilis webszerver küld vissza, és amelyet az adott URL tartalmának változásának meghatározására használnak.

Az ETagokat két dologra használhatjuk - gyorsítótárra és feltételes kérésekre. A Az ETag értéke hash-ként is felfogható a Response test bájtjaiból számítva. Mivel a szolgáltatás valószínűleg kriptográfiai kivonatoló funkciót használ, a test legkisebb módosítása is drasztikusan megváltoztatja a kimenetet és ezáltal az ETag értékét. Ez csak az erős ETagokra vonatkozik - a protokoll gyenge Etagot is biztosít.

Egy Ha-* fejléc a szokásos GET kérést feltételes GET-vé alakítja. A két Ha-* Az ETags-szal együtt használt fejlécek az „If-None-Match” és az „If-Match” - mindegyiknek megvan a maga szemantikája, amint azt a cikk később tárgyalja.

3. Ügyfél-kiszolgáló kommunikáció becsavar

Egy egyszerű kliens-szerver kommunikációt bonthatunk az ETag-okkal:

Először az ügyfél REST API-hívást indít - a Válasz tartalmazza az ETag fejlécet amelyeket további felhasználásra tárolunk:

curl -H "Accept: application / json" -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "f88dd058fe004909615a64f01be66a7" Content-Type: application / json; charset = UTF-8 Content-Length: 52

A következő kéréshez az Ügyfél felveszi a Ha-nincs-meccs kérjen fejlécet az előző lépés ETag értékével. Ha az erőforrás nem változott a szerveren, a válasz nem tartalmaz törzset és állapotkódot 304-ből - Nincs módosítva:

curl -H "Accept: application / json" -H 'If-None-Match: "f88dd058fe004909615a64f01be66a7"' -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 304 Nem módosított ETag: "f88dd058fe004909615a64f01be66a7"

Mielőtt újra lekérné az erőforrást, változtassunk rajta egy frissítés végrehajtásával:

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" Tartalom-hossz: 0

Végül kiküldjük az utolsó kérést a Foo visszaszerzésére. Ne feledje, hogy a legutóbbi kérés óta frissítettük, ezért a korábbi ETag-érték már nem működhet. A válasz tartalmazza az új adatokat és egy új ETag-et, amely ismét felhasználható:

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" Content-Type: application / json; charset = UTF-8 Content-Length: 56

És itt van - az ETags a vadonban, és megtakarítja a sávszélességet.

4. ETag támogatás tavasszal

Tovább a tavaszi támogatáshoz: az ETag használata tavasszal rendkívül egyszerűen beállítható és teljesen átlátható az alkalmazás számára. Engedélyezhetjük a támogatást egy egyszerű hozzáadásával Szűrő ban,-ben web.xml:

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

A szűrőt ugyanazon URI mintán térképezzük fel, mint maga a RESTful API. Maga a szűrő az ETag funkcionalitás standard megvalósítása a 3.0 tavasz óta.

A megvalósítás sekély - az alkalmazás a válasz alapján kiszámítja az ETag-t, ami megspórolja a sávszélességet, a szerver teljesítményét azonban nem.

Tehát, egy olyan kérés, amely részesül az ETag támogatásból, továbbra is standard kérésként kerül feldolgozásra, elfogyaszt minden olyan erőforrást, amelyet normálisan fogyasztana (adatbázis-kapcsolatok stb.), És az ETag támogatás csak azelőtt kapja meg a válaszát, hogy visszajuttatná a klienshez. ban ben.

Ekkor az ETag kiszámítása a Válasz testből történik, és magára az Erőforrásra lesz beállítva; is, ha a Ha-nincs-meccs fejléc volt beállítva a Request-en, azt is kezelni fogjuk.

Az ETag-mechanizmus mélyebb megvalósítása potenciálisan sokkal nagyobb előnyökkel járhat - például kiszolgálhat néhány kérést a gyorsítótárból, és egyáltalán nem kell elvégeznie a számítást -, de a megvalósítás határozottan nem lenne olyan egyszerű és nem is olyan plug-in, mint a sekély megközelítés itt leírt.

4.1. Java alapú konfiguráció

Lássuk, hogyan nézne ki a Java-alapú konfiguráció kijelentve a ShallowEtagHeaderFilter bab tavaszi kontextusunkban:

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

Ne feledje, hogy ha további szűrőkonfigurációkat kell megadnunk, akkor ehelyett kijelenthetjük a FilterRegistrationBean példa:

@Bean public FilterRegistrationBean shallowEtagHeaderFilter () {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean (új ShallowEtagHeaderFilter ()); filterRegistrationBean.addUrlPatterns ("/ foos / *"); filterRegistrationBean.setName ("etagFilter"); return filterRegistrationBean; }

Végül, ha nem a Tavaszi indítást használjuk, akkor a AbstractAnnotationConfigDispatcherServletInitializer’S getServletFilters módszer.

4.2. A ResponseEntity's használata eTag () Módszer

Ezt a módszert bevezették a 4.1 tavaszi keretbe, és használhatjuk az ETag érték vezérlésére, amelyet egyetlen végpont lekér.

Tegyük fel például, hogy verziószámos entitásokat használunk Optimist zárolási mechanizmusként az adatbázisunkhoz való hozzáféréshez.

Magát a verziót használhatjuk ETag-ként annak jelzésére, hogy az entitás módosult-e:

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

A szolgáltatás lekéri a megfelelőt 304-Nincs módosítva adja meg, hogy a kérés feltételes fejléce megegyezik-e a gyorsítótár adataival.

5. Az ETags tesztelése

Kezdjük egyszerűen - ellenőriznünk kell, hogy az egyetlen erőforrást lekérő egyszerű kérés válasza valóban visszaadja-e aETag ” fejléc:

@Test public void givenResourceExists_whenRetrievingResource_thenEtagIsAlsoReturned () {// Adott karakterlánc uriOfResource = createAsUri (); // When Response findOneResponse = RestAssured.given (). fejléc ("Elfogadás", "alkalmazás / json"). get (uriOfResource); // Ezután assertNotNull (findOneResponse.getHeader ("ETag")); }

Következő, ellenőrizzük az ETag viselkedés boldog útját. Ha a Kérés a letöltésére a Forrás a szerverről a helyeset használja ETag értéket, akkor a kiszolgáló nem tölti be az erőforrást:

@Test public void givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned () {// Adott karakterlánc uriOfResource = createAsUri (); Válasz findOneResponse = RestAssured.given (). fejléc ("Elfogadás", "alkalmazás / json"). get (uriOfResource); Karakterlánc etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); // Amikor a Response secondFindOneResponse = RestAssured.given (). header ("Accept", "application / json"). fejlécek ("If-None-Match", etagValue) .get (uriOfResource); // Ezután assertTrue (secondFindOneResponse.getStatusCode () == 304); }

Lépésről lépésre:

  • létrehozunk és beolvasunk egy erőforrást, tárolás a ETag érték
  • küldjön új lekérési kérelmet, ezúttal a “Ha-nincs-meccs”Fejléc a ETag korábban tárolt érték
  • erre a második kérésre a szerver egyszerűen visszaad egy a-t 304 Nincs módosítva, mivel maga az erőforrás valóban nem módosult a két visszakeresési művelet között

Végül ellenőrizzük azt az esetet, amikor az erőforrás megváltozik az első és a második lekérési kérelem között:

@Test public void givenResourceWasRetrievedThenModified_whenRetrievingAgainWithEtag_thenResourceIsReturned () {// Adott karakterlánc uriOfResource = createAsUri (); Válasz findOneResponse = RestAssured.given (). fejléc ("Elfogadás", "alkalmazás / json"). get (uriOfResource); Karakterlánc etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); existingResource.setName (randomAlphabetic (6)); frissítés (existingResource); // Amikor a Response secondFindOneResponse = RestAssured.given (). header ("Accept", "application / json"). fejlécek ("If-None-Match", etagValue) .get (uriOfResource); // Ezután assertTrue (secondFindOneResponse.getStatusCode () == 200); }

Lépésről lépésre:

  • először létrehozunk és visszakeresünk a Forrás - és tárolja a ETag érték további felhasználásra
  • akkor ugyanezt frissítjük Forrás
  • küldjön egy új GET kérést, ezúttal a “Ha-nincs-meccs”Fejléc a ETag amit korábban tároltunk
  • erre a második kérésre a szerver visszaadja a 200 OK a teljes erőforrással együtt, mivel a ETag Az érték már nem helyes, mivel időközben frissítettük az Erőforrást

Végül az utolsó teszt - amely nem fog menni, mert a funkcionalitást még nem hajtották végre tavasszal - az a. támogatása If-Match HTTP fejléc:

@Test public void givenResourceExists_whenRetrievedWithIfMatchIncorrectEtag_then412IsReceived () {// Adott T existingResource = getApi (). Create (createNewEntity ()); // Amikor a karakterlánc uriOfResource = baseUri + "/" + existingResource.getId (); Válasz findOneResponse = RestAssured.given (). Fejléc ("Elfogadás", "alkalmazás / json"). fejlécek ("If-Match", randomAlphabetic (8)). get (uriOfResource); // Ezután assertTrue (findOneResponse.getStatusCode () == 412); }

Lépésről lépésre:

  • létrehozunk egy Erőforrást
  • majd töltse le a „If-Match”Címsor hibásat ad meg ETag érték - ez egy feltételes GET kérés
  • a szervernek vissza kell adnia a 412 Nem sikerült az előfeltétel

6. Az ETags nagyok

Csak olvasási műveletekhez használtunk ETag-okat. RFC létezik, amely megpróbálja tisztázni, hogy miként kell a megvalósításoknak kezelniük az ETagokat az írási műveleteknél - ez nem szokványos, de érdekes olvasmány.

Természetesen az ETag mechanizmusnak más lehetőségei is vannak, mint például egy optimista zárszerkezet, valamint a kapcsolódó „Elveszett frissítési probléma” kezelése.

Az ETags használatakor számos ismert lehetséges buktató és figyelmeztetés is van.

7. Következtetés

Ez a cikk csak a felületet karcolta meg azzal, ami a Spring és az ETags segítségével lehetséges.

Az ETag-kompatibilis RESTful szolgáltatás teljes körű megvalósításához, valamint az ETag viselkedését igazoló integrációs tesztekkel együtt nézze meg a GitHub projektet.

REST alsó

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT