תגי REST עם אביב

REST למעלה

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

1. סקירה כללית

מאמר זה יתמקד ב עובדים עם ETags באביב, בדיקת שילוב של ה- REST API ותרחישים של צריכת עם סִלְסוּל.

2. REST ו- ETags

מתיעוד האביב הרשמי בנושא תמיכת ETag:

ETag (תגית ישות) הוא כותרת תגובת HTTP המוחזרת על ידי שרת אינטרנט תואם HTTP / 1.1 המשמש לקביעת שינוי בתוכן בכתובת URL נתונה.

אנו יכולים להשתמש ב- ETags לשני דברים - אחסון במטמון ובקשות מותנות. ה ניתן לחשוב על ערך ETag כ- hash מחושב מתוך הבתים של גוף התגובה. מכיוון שהשירות ככל הנראה משתמש בפונקציית חשיש קריפטוגרפית, אפילו השינוי הקטן ביותר בגוף ישנה באופן דרסטי את התפוקה וכך את ערך ה- ETag. זה נכון רק לגבי ETags חזקות - הפרוטוקול מספק גם Etag חלש.

שימוש ב- אם-* כותרת הופכת בקשת GET רגילה ל- GET מותנה. השניים אם-* כותרות שמשתמשות ב- ETags הן "אם-אין-התאמה" ו"אם-התאמה "- כל אחת מהן סמנטיקה משלה כפי שנדון בהמשך מאמר זה.

3. תקשורת לקוח-שרת עם סִלְסוּל

אנו יכולים לפרק תקשורת פשוטה של ​​לקוח-שרת הכוללת ETags לשלבים:

ראשית, הלקוח מבצע שיחת REST API - התגובה כוללת את כותרת ETag אשר יאוחסנו לשימוש נוסף:

תלתל -H "קבל: יישום / json" -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "f88dd058fe004909615a64f01be66a7" סוג תוכן: יישום / json; טקסט = UTF-8 אורך תוכן: 52

לבקשה הבאה הלקוח יכלול את אם אין משחק כותרת בקשה עם ערך ETag מהשלב הקודם. אם המשאב לא השתנה בשרת, התגובה לא תכיל גוף וקוד סטטוס מתוך 304 - לא שונה:

תלתל -H "קבל: יישום / json" -H 'אם-ללא-התאמה: "f88dd058fe004909615a64f01be66a7"' -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 304 לא שונה ETag: "f88dd058fe004909615a64f01be66a7"

כעת, לפני שנאחזר את המשאב שוב, בואו נשנה אותו על ידי ביצוע עדכון:

תלתל -H "סוג תוכן: יישום / json" -i -X ​​PUT - נתונים '{"id": 1, "name": "Transformers2"}' // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "d41d8cd98f00b204e9800998ecf8427e" אורך תוכן: 0

לבסוף, אנו שולחים את הבקשה האחרונה לאחזר את ה- Foo שוב. יש לזכור שעדכנו אותו מאז הפעם האחרונה שביקשנו זאת, כך שערך ה- ETag הקודם כבר לא אמור לעבוד. התגובה תכיל את הנתונים החדשים ו- ETag חדש, ששוב ניתן לאחסן לשימוש נוסף:

תלתל -H "קבל: יישום / json" -H 'אם-ללא-התאמה: "f88dd058fe004909615a64f01be66a7"' -i // localhost: 8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETag: "03cb37ca667706c68c0aad4cb04c3a211" סוג תוכן: יישום / json; charset = UTF-8 אורך תוכן: 56

והנה לך - ETags בטבע ורוחב פס חוסך.

4. תמיכת ETag באביב

על התמיכה באביב: השימוש ב- ETag באביב קל מאוד להגדרה ושקוף לחלוטין ליישום. אנו יכולים לאפשר את התמיכה על ידי הוספת פשוט לְסַנֵן בתוך ה web.xml:

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

אנו ממפים את המסנן באותו דפוס URI כמו ממשק ה- API RESTful עצמו. המסנן עצמו הוא הטמעה סטנדרטית של פונקציונליות ETag מאז אביב 3.0.

היישום הוא רדוד - היישום מחשב את ה- ETag על סמך התגובה, שתחסוך רוחב פס אך לא ביצועי שרת.

לכן, בקשה שתפיק תועלת מתמיכת ETag עדיין תעבוד כבקשה רגילה, תצרוך כל משאב שהיא תצרוך בדרך כלל (חיבורי מסד נתונים וכו ') ורק לפני שתשובתה תשוב ללקוח תחזור התמיכה של ETag ב.

בשלב זה ה- ETag יחושב מתוך גוף התגובה ויוגדר על המשאב עצמו; גם אם אם אין משחק כותרת הוגדרה על פי הבקשה, היא תטופל גם כן.

יישום עמוק יותר של מנגנון ETag עשוי לספק יתרונות גדולים בהרבה - כמו למשל הגשת בקשות מהמטמון ולא צורך לבצע את החישוב בכלל - אך היישום בהחלט לא יהיה פשוט, וגם לא תומך כמו הגישה הרדודה. מתואר כאן.

4.1. תצורה מבוססת Java

בואו נראה איך התצורה מבוססת Java תיראה הכרזה א ShallowEtagHeaderFilter שעועית בהקשר האביב שלנו:

@Bean הציבור ShallowEtagHeaderFilter shallowEtagHeaderFilter () {להחזיר ShallowEtagHeaderFilter חדש (); }

זכור שאם נצטרך לספק תצורות סינון נוספות, נוכל להכריז על a FilterRegistrationBean למשל:

@Rean FilterRegistrationBean shallowEtagHeaderFilter ציבורי () {FilterRegistrationBean filterRegistrationBean = FilterRegistrationBean חדש (ShallowEtagHeaderFilter חדש ()); filterRegistrationBean.addUrlPatterns ("/ foos / *"); filterRegistrationBean.setName ("etagFilter"); להחזיר filterRegistrationBean; }

לבסוף, אם איננו משתמשים באביב אתחול, אנו יכולים להגדיר את המסנן באמצעות ה- תקציר AnnotationConfigDispatcherServletInitializerשל getServletFilters שיטה.

4.2. שימוש ב- ResponseEntity eTag () שיטה

שיטה זו הוצגה במסגרת האביב 4.1, ו- אנו יכולים להשתמש בו כדי לשלוט בערך ה- ETag שנקודת קצה אחת מאחזרת.

לדוגמה, דמיין שאנו משתמשים בישויות גרסאיות כמנגנון נעילת אופטימיסט כדי לגשת למידע מסד הנתונים שלנו.

אנו יכולים להשתמש בגרסה עצמה כ- ETag כדי לציין אם הישות שונתה:

@GetMapping (value = "/ {id} / custom-etag") ResponseEntity ציבורי findByIdWithCustomEtag (@PathVariable ("id") מזהה ארוך סופי) {// ... Foo foo = ... להחזיר ResponseEntity.ok (). eTag (Long.toString (foo.getVersion ())). גוף (foo); }

השירות יאחזר את המקביל 304-לא שונה ציין אם הכותרת המותנית של הבקשה תואמת את נתוני המטמון.

5. בדיקת ETags

נתחיל בפשטות - עלינו לוודא שהתגובה של בקשה פשוטה לאחזור משאב יחיד אכן תחזיר את "ETag ” כּוֹתֶרֶת:

@Test הציבור בטל givenResourceExists_whenRetrievingResource_thenEtagIsAlsoReturned () {// נתון מחרוזת uriOfResource = createAsUri (); // When Response findOneResponse = RestAssured.given (). כותרת עליונה ("קבל", "יישום / json"). get (uriOfResource); // ואז assertNotNull (findOneResponse.getHeader ("ETag")); }

הַבָּא, אנו מאמתים את הדרך המאושרת של התנהגות ETag. אם הבקשה לאחזר את מַשׁאָב מהשרת משתמש נכון ETag ערך, ואז השרת לא מאחזר את המשאב:

@ מבחן הריק ציבורי givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned () {// ניתן מחרוזת uriOfResource = createAsUri (); תגובה findOneResponse = RestAssured.given (). כותרת עליונה ("קבל", "יישום / json"). get (uriOfResource); מחרוזת etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); // מתי תגובה secondFindOneResponse = RestAssured.given (). כותרת עליונה ("קבל", "יישום / json"). כותרות ("אם אין התאמה", etagValue) .get (uriOfResource); // ואז assertTrue (secondFindOneResponse.getStatusCode () == 304); }

צעד אחר צעד:

  • אנו יוצרים ומשחזרים משאבים, אחסון ה ETag ערך
  • שלח בקשת אחזור חדשה, הפעם עם הכיתוב "אם אין משחקכותרת המציינת את ETag ערך שנשמר בעבר
  • בבקשה שנייה זו, השרת פשוט מחזיר a 304 לא שונהמאחר שהמשאב עצמו אכן לא השתנה בין שתי פעולות האחזור

לבסוף, אנו מאמתים את המקרה בו המשנה משתנה בין בקשת האחזור הראשונה לשנייה:

@Test הציבור בטל givenResourceWasRetrievedThenModified_whenRetrievingAgainWithEtag_thenResourceIsReturned () {// נתון מחרוזת uriOfResource = createAsUri (); תגובה findOneResponse = RestAssured.given (). כותרת עליונה ("קבל", "יישום / json"). get (uriOfResource); מחרוזת etagValue = findOneResponse.getHeader (HttpHeaders.ETAG); existingResource.setName (randomAlphabetic (6)); עדכון (existentResource); // מתי תגובה secondFindOneResponse = RestAssured.given (). כותרת עליונה ("קבל", "application / json"). כותרות ("If-None-Match", etagValue) .get (uriOfResource); // ואז assertTrue (secondFindOneResponse.getStatusCode () == 200); }

צעד אחר צעד:

  • ראשית אנו יוצרים ומאחזרים א מַשׁאָב ולאחסן את ETag ערך לשימוש נוסף
  • ואז אנו מעדכנים אותו דבר מַשׁאָב
  • שלח בקשת GET חדשה, הפעם עם הכיתוב “אם-אין-התאמהכותרת המציינת את ETag שאחסנו בעבר
  • בבקשה שנייה זו, השרת יחזיר א 200 בסדר יחד עם המשאב המלא, מאז ETag הערך כבר לא נכון, כיוון שעדכנו את המשאב בינתיים

לבסוף, המבחן האחרון - שלא עומד לעבוד מכיוון שהפונקציונליות טרם יושמה באביב - הוא התמיכה ב- אם-התאמה כותרת HTTP:

@Test ציבורי בטל givenResourceExists_whenRetrievedWithIfMatchIncorrectEtag_then412IsReceived () {// נתון T existentResource = getApi (). Create (createNewEntity ()); // כאשר מחרוזת uriOfResource = baseUri + "/" + existingResource.getId (); תגובה findOneResponse = RestAssured.given (). כותרת ("קבל", "application / json"). כותרות ("If-Match", randomAlphabetic (8)). get (uriOfResource); // ואז assertTrue (findOneResponse.getStatusCode () == 412); }

צעד אחר צעד:

  • אנו יוצרים משאבים
  • ואז אחזר אותו באמצעות "אם-התאמהכותרת המציינת שגוי ETag ערך - זו בקשת GET מותנית
  • השרת אמור להחזיר a 412 תנאי מוקדם נכשל

6. ETags גדולים

השתמשנו רק בתגי ET עבור פעולות קריאה. RFC קיים ומנסה להבהיר כיצד יישומים צריכים להתמודד עם ETags על פעולות כתיבה - זה לא סטנדרטי, אבל הוא קריאה מעניינת.

ישנם כמובן שימושים אפשריים אחרים במנגנון ETag, כגון במנגנון נעילה אופטימי וכן התמודדות עם "בעיית עדכון אבוד" הקשורה.

ישנם גם מספר מלכודות פוטנציאליות ידועות שכדאי לדעת כאשר משתמשים ב- ETags.

7. מסקנה

מאמר זה רק גירד את פני השטח עם מה שאפשר עם Spring ו- ETags.

למימוש מלא של שירות RESTful עם ETag, יחד עם מבחני שילוב המאמתים את התנהגות ETag, עיין בפרויקט GitHub.

REST תחתון

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

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