HATEOAS לשירות REST באביב

REST למעלה

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

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

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

מאמר זה יתמקד ב יישום גילוי בשירות Spring REST ועל סיפוק האילוץ של HATEOAS.

מאמר זה מתמקד ב- MVC באביב. המאמר שלנו Intro to Spring HATEOAS מתאר כיצד להשתמש ב- HATEOAS ב- Spring Boot.

2. ניתוק ניתוק באמצעות אירועים

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

ראשית, בואו ניצור את האירועים:

מחלקה ציבורית SingleResourceRetrieved מאריך ApplicationEvent {תגובה HttpServletResponse פרטית; ציבורי SingleResourceRetrieved (מקור אובייקט, תגובת HttpServletResponse) {super (source); this.response = תגובה; } ציבורי HttpServletResponse getResponse () {תגובה תשובה; }} מחלקה ציבורית ResourceCreated מרחיבה את ApplicationEvent {תגובה HttpServletResponse פרטית; פרטי ארוך idOfNewResource; public ResourceCreated (מקור אובייקט, תגובה HttpServletResponse, ארוך idOfNewResource) {super (source); this.response = תגובה; this.idOfNewResource = idOfNewResource; } ציבורי HttpServletResponse getResponse () {תגובה תשובה; } ציבורי ארוך getIdOfNewResource () {להחזיר idOfNewResource; }}

לאחר מכן, הבקר, עם שתי פעולות פשוטות - למצוא לפי מזהה ו לִיצוֹר:

@RestController @RequestMapping (value = "/ foos") FooController בכיתה ציבורית {@Autowired פרטי ApplicationEventPublisher eventPublisher; @ שירות IFooService פרטי מאושרים; @GetMapping (value = "foos / {id}") Foo findById ציבורי (@PathVariable ("id") מזהה ארוך, תגובת HttpServletResponse) {Foo resourceById = Preconditions.checkNotNull (service.findOne (id)); eventPublisher.publishEvent (SingleResourceRetrieved חדש (זה, תגובה)); להחזיר resourceById; } @PostMapping @ResponseStatus (HttpStatus.CREATED) יצירת חלל ציבורי (משאב @RequestBody Foo, תגובה HttpServletResponse) {Preconditions.checkNotNull (משאב); ארוך newId = service.create (משאב) .getId (); eventPublisher.publishEvent (ResourceCreated חדש (זה, תגובה, newId)); }}

לאחר מכן נוכל להתמודד עם אירועים אלה עם מספר כלשהו של מאזינים מנותקים. כל אחד מאלה יכול להתמקד במקרה הספציפי שלו ולעזור לקיים את האילוץ הכללי של HATEOAS.

המאזינים צריכים להיות האובייקטים האחרונים בערמת השיחות ואין צורך בגישה ישירה אליהם; ככאלה הם אינם ציבוריים.

3. גילוי ה- URI של משאב חדש שנוצר

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

אנו נתמודד עם זה באמצעות מאזין:

מחלקה @Component ResourceCreatedDiscoverabilityListener מיישם את ApplicationListener {@Override public void onApplicationEvent (ResourceCreated resourceCreatedEvent) {Preconditions.checkNotNull (resourceCreatedEvent); HttpServletResponse response = resourceCreatedEvent.getResponse (); ארוך idOfNewResource = resourceCreatedEvent.getIdOfNewResource (); addLinkHeaderOnResourceCreation (תגובה, idOfNewResource); } void addLinkHeaderOnResourceCreation (HttpServletResponse response, long idOfNewResource) {URI uri = ServletUriComponentsBuilder.fromCurrentRequestUri (). נתיב ("/ {idOfNewResource}"). buildAndExpand (idOfNewResource) .toUri (); response.setHeader ("מיקום", uri.toASCIIString ()); }}

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

אם ה- API יחזור תגובה תגובה נוכל גם להשתמש ב- מקום תמיכה.

4. קבלת משאב יחיד

בעת אחזור משאב יחיד, הלקוח אמור להיות מסוגל לגלות את ה- URI כדי להשיג את כל המשאבים מהסוג הזה:

מחלקה @Component SingleResourceRetrievedDiscoverabilityListener מיישם את ApplicationListener {@Override public void onApplicationEvent (SingleResourceRetieved resourceRetrievedEvent) {Preconditions.checkNotNull (resourceRetrievedEvent); HttpServletResponse response = resourceRetrievedEvent.getResponse (); addLinkHeaderOnSingleResourceRetrieval (בקשה, תגובה); } void addLinkHeaderOnSingleResourceRetrieval (HttpServletResponse response) {String requestURL = ServletUriComponentsBuilder.fromCurrentRequestUri (). build (). toUri (). toASCIIString (); int positionOfLastSlash = requestURL.lastIndexOf ("/"); מחרוזת uriForResourceCreation = requestURL.substring (0, positionOfLastSlash); מחרוזת linkHeaderValue = LinkUtil .createLinkHeader (uriForResourceCreation, "אוסף"); response.addHeader (LINK_HEADER, linkHeaderValue); }}

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

ה קישור כותרת היא אחד מכותרות ה- HTTP הנפוצות ביותרלמטרות גילוי. כלי השירות ליצירת כותרת זו הוא פשוט מספיק:

מחלקה ציבורית LinkUtil {מחרוזת סטטית ציבורית createLinkHeader (מחרוזת uri, מחרוזת rel) {return "; rel = \" "+ rel +" \ ""; }}

5. גילוי בשורש

השורש הוא נקודת הכניסה בשירות כולו - זה מה שהלקוח בא במגע עם צריכת ה- API בפעם הראשונה.

אם יש לשקול וליישם את אילוצי HATEOAS לאורך כל הדרך, זה המקום להתחיל בו. לָכֵן כל ה- URI העיקריים של המערכת צריכים להיות ניתנים לגילוי מהשורש.

בואו נסתכל על הבקר לכך:

@GetMapping ("/") @ ResponseStatus (value = HttpStatus.NO_CONTENT) admin void publicRoot (סופי HttpServletRequest בקשה, סופי HttpServletResponse תגובה) {String rootUri = request.getRequestURL (). ToString (); URI fooUri = UriTemplate חדש ("{rootUri} {resource}"). הרחב (rootUri, "foos"); מחרוזת linkToFoos = LinkUtil.createLinkHeader (fooUri.toASCIIString (), "אוסף"); response.addHeader ("קישור", linkToFoos); }

זה, כמובן, המחשה של הרעיון, המתמקד ב- URI יחיד, לדוגמא פו אֶמְצָעִי. יישום אמיתי אמור להוסיף, באופן דומה, URI עבור כל המשאבים שפורסמו ללקוח.

5.1. גילוי אינו קשור לשינוי URI

זו יכולה להיות נקודה שנויה במחלוקת - מצד אחד, מטרת HATEOAS היא שהלקוח יגלה את ה- URI של ה- API ולא יסתמך על ערכים מקודדים. מצד שני - לא כך האינטרנט עובד: כן, מתגלים URI, אך הם גם מסומנים בסימניות.

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

לסיכום - רק מכיוון שכל ה- URI של שירות האינטרנט RESTful צריך להיחשב כ- URI מגניב (ו- URI מגניב לא משתנה) - אין זה אומר כי עמידה במגבלת HATEOAS אינה שימושית במיוחד בעת פיתוח ה- API.

6. אזהרות של גילוי

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

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

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

7. מסקנה

מאמר זה כיסה את יישום חלק מתכונות הגילוי בהקשר של שירות RESTful עם Spring MVC ונגע במושג הגילוי בשורש.

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

REST תחתון

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

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

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