במאמר הקרוב נדבר על עשרת הטעויות הנפוצות של REST API (נבין כמובן את ה-Best Practice). ממשקי REST API הם הנפוצים והפופולריים ביותר מבין אפשרויות ממשקי ה-API, הם (אמורים להיות) קלים ליצירה ותפעול אך למרות הפשטות הזו – כמו בכל דבר, עלינו לחשוב היטב כאשר אנו מתכננים ומעצבים אותם. כשמפתחים בסביבות פיתוח עמוסות לצערי הרב אנו עלולים למצוא שם טעויות רבות שמפתחים מהעבר או אפילו אנחנו (אאווץ’) הוספנו בתמימות כדי לסיים משימה מהר. בפוסט הקרוב נחקור כמה מהשגיאות הנפוצות ביותר שנעשו על ידי מפתחי API ונבין כיצד ניתן למנוע אותן וגם כיצד נכון יותר לעבוד.
תוכן עניינים
מה זה API?
API הוא קיצור של Application Programming Interface. זהו ממשק תוכנה ויש גם שיקראו לזה “חוזה” המאפשר לשני יישומים (או יותר) לתקשר אחד עם השני ללא התערבות משתמש. ממשקי API מאפשרים למוצרים או שירותים לתקשר עם שירותים אחרים בלי תלויות וגם מבלי יכולת לדעת איך הם ממומשים.
מה זה REST?
REST (ראשי תיבות: Representational State Transfer) הוא סגנון ארכיטקטוני למימוש אינטרקציית Client-Server, מדובר בקבוצה של אילוצים שעל ידי השימוש בהם נוכל ליצור אפליקציות אינטרנט כאשר REST API היא דרך לגשת לשירותי אינטרנט בצורה פשוטה וגמישה ללא כל עיבוד.
טכנולוגיית REST היא המועדפת בדרך כלל על פני טכנולוגיית SOAP מכיוון ש-REST משתמש בפחות משאבים, פחות רוחב פס, הוא פשוט וגמיש מה שהופך אותו למתאים יותר לשימוש באינטרנט. הוא משמש כדי להביא או לתת מידע מסוים משירות אינטרנט. כל התקשורת הנעשית באמצעות REST API משתמשת רק בבקשת HTTP.
ב-HTTP ישנן חמש מוסכמות כשאנו ממשמים בארכיטקטורה מבוססת REST, אלה מתאימות לפעולות יצירה, קריאה, עדכון ומחיקה (או CRUD) בהתאמה:
GET – נשתמש בו כשנרצה לקבל מידע מהשרת.
POST – שולח מידע אל השרת.
PUT – משמש לעדכון של מידע שכבר קיים.
DELETE – מייצג בקשות מחיקה של מידע מהמערכת.
ממשקי REST API תפסו את מקומם בפיתוח אתרים והם כאן כדי להישאר. כמעט כל יישום אינטרנט הטמיע סוג של שירותי REST, בין אם לשימוש פנימי או אינטגרציה עם מערכת חיצונית. אבל אנחנו עדיין רואים דפוסים ושירותים של RESTful שבורים שאינם עומדים בשיטות הצפויות.ק
1. RESTful Anti Patterns
שימוש בפעולת HTTP שגוייה, אין שום הצדקה שבעולם אם ביצעתם שימוש ב-GET כדי לכתוב נתונים, זה אמנם אפשרי, הקוד יעבוד בלי לכעוס עליכם אך ממש לא בטוח להעביר ככה נתונים למערכת.
בכוונה בחרתי להתחיל עם זה, מדובר בטעות שהיא דיי בסיסית וכזו שאסור לכם ליפול בה.
השגיאה היותר נפוצה שמפתחים נופלים בה היא התבלבלות בין PUT לPOST שהם מעט דומים, תשקיעו שתי דקות בלהבין מה ההבדלים בינהם וזה יספיק כדי לעבוד נכון.
2. הודעות שגיאה לא מועילות
זה בכלל לא משנה אם הלקוחות של הAPI שלכם הם ממש לקוחות אמיתיים או צוותים אחרים בחברה, זה סופר חשוב לממש הודעות שגיאה לפי מקרי קצה. וממה שיצא לי לראות לא מעט השגיאה הזו מתחלקת ל2 – אלה שכותבים הודעות שגיאה לא מספקות (כאלה שצריך לפתוח אחריהם לוגים כדי להבין מה קרה) ואלה שעוד יותר מגזימים ולא כותבים בכלל,
מדובר ביכולת חשובה מאוד כשאנו מתכננים API, היכולת לזרוק למשתמש שגיאות רלוונטיות שימחישו מה באמת קרה במידה והבקשה לא הצליחה מסיבה כזו או אחרת, ויש כל מיני סיבות.
200 OK
התגובה המוכרת ביותר, מה היא אומרת? הכל טוב! רק תדאגו לספק את הקוד הזה רק אם הכל באמת בסדר.
400 Bad Request
כמעט בכל המקרים זה נובע משגיאת בקלט המשתמש. אל תסתפקו בזה, רצוי לוודא שהודעת השגיאה שלך מספקת כמה פרטים לגבי הקלט הפגום כדי שהמשתמש יבין מה הוא עשה לא בסדר ולתקן אותה במהירות.
401 Unauthorized
מצב זה אומר שהקלט בסדר אבל בבקשת המשתמשים חסר קוד הרשאה. אל תתבלבלו עם:
403 Forbidden
משמעות הדבר היא שקוד ההרשאה מזוהה כתקף אך למרות זאת למשתמש אין הרשאה. לדוגמה, משתמש מנסה לגשת לחלק שזמין רק למנהלי המערכת.
404 Not Found
המשתמש ביקש מידע מסויים ולא מצאנו אותו.
429 Too Many Requests
זה קורה כאשר אותו משתמש מנסה לשלוח המון בקשות ל-API יותר מדי פעמים ברצף מהיר. לאחר מספר התקפות DDoS בפרופיל גבוה בעשור האחרון, שירותי האינטרנט עוקבים מקרוב אחר מי ניגשים לשרת שלהם ובאיזו תדירות.
האמת היא שאת זה פחות נהוג לממש כחלק מAPI מסויים אלא יותר במעטפת, לרוב הגנות אלו מובנות כשמשתמשים בפריימוורקים.
5xx API Errors
כשקוד הסטטוס מתחיל ב-5 אנו מיד יודעים שמדובר בשגיאות שרת. אם זה המקרה, ודא שהודעות השגיאה שלך מספקות עזרה למשתמש. זה יכול להיות מידע ליצירת קשר או דף עם מידע בזמן פעילות/זמן השבתה בזמן אמת.
ממליץ לכם לצפות בסרטון הזה כדי להבין את זה לעומק: https://www.youtube.com/watch?v=MfTLob6teJE&ab_channel=CodeOpinion
3. לא עומד ב-Scaling
אין כאן דוגמא אחת שתמחיש את כל המקרים אך באופן כללי תמיד ננסה לחשוב על העתיד, על מה יקרה כש…
נניח שאת.ה בוחר לאחסן תמונות פרופיל שהועלו על ידי המשתמשים שלך ישירות לשרת אינטרנט. זהו פתרון תקף לחלוטין – קבצים נגישים במהירות לאפליקציה, שיטות טיפול בקבצים זמינות בכל פלטפורמת פיתוח, ותוכלו אפילו להגיש תמונות אלו כתוכן סטטי, כלומר עומס מינימלי על האפליקציה שלכם.
אבל מה קורה כשהאפליקציה שלך גדלה, ואתה צריך להשתמש בשני שרתי אינטרנט או יותר מאחורי מאזן עומסים? למרות שדאגת להגדיל את אחסון מסד הנתונים (נקודה למחשבה), כאן נפלת בסקיילביליות, לפיכך, עליך ליישם פתרון מסויים של סנכרון קבצים בין השרתים השונים ולא להיות תלוי בשרת הראשוני
מה שעליך לעשות כדי למנוע את הבעיה מלכתחילה היה פשוט להשתמש באחסון קבצים משותף, מסד נתונים או כל פתרון אחסון מרוחק אחר. זה כנראה היה מוסיף כמה שעות עבודה נוספות ליישם את הכל, אבל זה שווה את הטרחה.
4. לא דאגתם לאימות/ולדיציה של הקלט
אימות קלט משתמש הן בצד הלקוח והן בשרת הוא פשוט צעד שחובה לעשות! כולנו מודעים לעצת החכמים “אל תסמוך על קלט משתמש”, אך עם זאת, טעויות הנובעות מאימות קורות לעתים קרובות מדי. למה שנרצה לתת לאפליקציה “להתמאץ” ולבזבז בכלל זמן כשאפשר בשורה הראשונה להבין שמשהו לא בסדר?
ומה קורה כשהמשתמש מכניס מידע חדש למערכת, האם נרצה לתת לזה לקרות?
זכרו שרוב פלטפורמות פיתוח צד-שרת הפופולאריות מספקות פתרונות כאלה ואחרים ומשתמשות בהערות פשוטות כדי להבטיח שהנתונים שהוגשו עומדים בכללים הצפויים. הטמעת אימות עשויה להיות גוזלת זמן, אבל זה צריך להיות חלק מגישת הפיתוח.
5. פורמט URI, השתמשו בשמות עצם ולא בפעלים
ההבדל העיקרי בין APIs בסגנון REST לבין APIs בסגנון RPC/SOAP הוא ההבדל בין שמות עצם ופעלים. ממשקי API של RESTful מבוססים על שמות עצם, הימנעו מלהשתמש בפעלים.
דוגמא לשימוש בפעלים:
https://your.site.com/getAllBlogPosts
https://your.site.com/updateBlogPost/12
https://your.site.com/deleteBlogPost/12
https://your.site.com/getAuthorById/3
https://your.site.co/deleteAuthorm/3
https://your.site.com/updateAuthor/3
לעומת:
HTTP GET https://your.site.co/blogposts – gets all the blog posts
HTTP GET https://your.site.co/blogposts/12 – gets the blog post with the id 12
HTTP POST https://your.site.co/blogposts – adds a new blog post and returns the details
HTTP DELETE https://your.site.co/blogposts/12 – removes the blog post with the id 12
HTTP GET https://your.site.co/authors/3/blogposts – gets all the blog posts of the author with id 3
זוהי דרך נקייה, נכונה ומדויקת יותר ליצור API. זה הרי ברור מיד למשתמש הקצה שאם הוא מבצע קריאת DELETE הוא מצפה למחיקה וכן הלאה.
6. הבקשה שלכם לא מחזירה JSON
אם הAPI שלכם מחזיר כל דבר שהוא לא JSON זו נורה אדומה קריטית, אל תכניסו ואל תאשרו קוד כזה. ואם לא הייתי ברור אז בואו נבהיר את זה רגע אחת ולתמיד – ממשקי API של REST חייבים לקבל JSON לא משנה אם מדובר בבקשה או בשליחה. JSON הוא התקן המקובל להעברת נתונים. כמעט כל שפה וכל טכנולוגיה תדע להשתמש בה: ל-JavaScript יש שיטות מובנות לקידוד ולפענוח של JSON דרך ה-Fetch API או לקוח HTTP אחר. לטכנולוגיות בצד השרת יש ספריות שיכולות לפענח JSON מבלי לעשות הרבה מאמץ.
חשוב לציין שיש החרגה אחת ויחידה והיא (ניחשתם נכון) אם אנחנו מנסים לשלוח ולקבל קבצים בין לקוח לשרת. רק אז אנחנו צריכים לטפל בלהחזיר קבצים אך זה כבר נושא לפעם אחרת.
בשורה התחתונה עלינו גם לוודא שנקודות הקצה שלנו מחזירות JSON כתגובה. למסגרות רבות בצד השרת יש את זה כתכונה מובנית.
7. היזהרו להתעסק עם שינויים בAPI שכבר קיימים
בין אם אתם מסוג המפתחים שרואים קוד מקולקל וישר רצים לתקן ובין אם נדרש מכם לתקן באג מסויים, זכרו תמיד שבכל פעם שאתם נוגעים במשהו שכבר קיים, ככל הנראה יש גם הרבה אינטגרציות שכבר מכירות אותו ומסתמכות על החוזה הראשוני שנקבע. שינוי או הסרה של רכיבים יכולים להשפיע באופן קריטי על שילובים מסוימים. הסיכון גבוה במיוחד כאשר אתה מנסה לייעל ממשק API מדור קודם –
אם בכל זאת נדרשתם לעשות זאת, תדאגו בשום פנים ואופן שלא להחליף אובייקטים מוחזרים או את אלה שבקלט, מה כן ניתן לעשות? אפשר להרחיב את הקיימים או ליצור גרסה חדשה לAPI כפי שנהוג בלא מעט סביבות פיתוח.
8. הגזמתם, יש לכם יותר מדי EndPoints במערכת
הטעות הזו קורית לא מעט, יצא לי לראות בעיניים ואפילו ליפול בזה בתחילת דרכי. אתם מביאים מידע שקשור לuser מסויים? שרשרו את זה על users/data, בנוסף לכך כפי שכתבתי בסעיף 5 – אין טעם לכתוב נתיב שונה לכל פעולה – הכתובת הזו: https://mysite.com/users/id תוכל לשמש גם לצפייה במידע, גם להוספה וגם למחיקה, אין סיבה שיהיה לכם דברים מוזרים בקוד כמו https://mysite.com/getUserById
רצוי לשמור על גנריות כמה שאפשר, יש קונבנציה שנהוג לעבוד בה, לעתים קרובות, נקודות קצה שונות יכולות להיות מקושרות זו בזו, לכן כדאי לקנן אותן כדי שיהיה קל יותר להבין אותן. לרוב הטעות הזו קורית כשלא עובדים לפי המוסכמות למשל אם נרצה להביא משתמש מסויים נעשה זאת ככה: https://mysite.com/users/id ואם נרצה להביא את כולם נעשה זאת כך: https://mysite.com/users , שימו לב שבשני המקרים כתוב ברבים users.
בואו נסתכל על דוגמה נוספת: פלטפורמת בלוגים מרובת משתמשים, פוסטים שונים יכולים להיכתב על ידי כותבים שונים, כך שנקודת קצה כגון https://mysite.com/posts/author תעשה קינון חוקי במקרה זה.
באותו אופן, לפוסטים עשויים להיות הערות בודדות שלהם, אז כדי לאחזר את ההערות, נקודת קצה כמו https://mysite.com/posts/postId/comments תהיה נתיב הגיוני.
* לא חובה אך כדאי להימנע מקינון בעומק של יותר מ-3 רמות מכיוון שזה יכול להפוך את ה-API לפחות אלגנטי וקריא.
9. שיתוף יתר של נתונים
שיתוף יתר של נתונים יכול לחסוך אמנם זמן פיתוח, אך בטווח הארוך, זה יכול ליצור אפילו יותר בעיות. אם אתה צריך לשלוח מידע כלשהו כאובייקט כתגובה, ייתכן שתתפתה לשלוח את האובייקט כולו במקום רק את המאפיינים הדרושים.
נניח שה-API שלך צריך לשלוח מידע בנוגע למלון: שמו, כתובתו, ולמה לא, מספר הטלפון של הקבלה. מסד הנתונים היה נדיב ובאמצעות שאילתה יחידה, חייבים להיזהר ממידע מיותר, תיצרו אובייקטי DTO חדשים אם צריך שלא יכילו סיסמאות ודברים רגישים שלא התבקשתם באמת להחזיר.
10. שכחתם משכבת אבטחה
זה כבר נושא גדול לפוסט שלם שאולי ייכתב בהמשך אך בכל זאת חובה לדבר עליו.
כתבתם API רגיש ושכחתם להוסיף שכבת אבטחה, רמת הרשאות, אותנטיקציה או כל דבר שאפשר כדי שלא כל מי שמכיר את הנתיב יוכל לגעת במידע או רחמנא לצלן לבצע פעולות אסורות במערכת.
אנחנו צריכים תמיד לנסות לשמור על התקשורת בין הלקוח לשרת פרטית.
נסו לאמת פרמטרים של כל בקשה כבר מההתחלה. יישמו בדיקות אימות ותדאגו לחסום כל בקשה שאינה עוברת את האימות הרלוונטי. אם עושים את הדברים נכון זה מונע אולי חלק מהתקפות הסייבר האכזריות שעלולות לפגוע בשרתים שלך.
אתם יכולים לקחת את אבטחת האימות שלך צעד קדימה על ידי הטמעת OAuth 2.0 או בעזרת כל אפליקציות של צד שלישי, תוכל ליצור סביבה מאובטחת יותר עבור המשתמשים שלך.
כמו כן השתמשו בפרוטוקול HTTPS שכולל SSL, לעולם אל תחשפו מידע עדין, כמו שמות משתמש, סיסמאות, מפתחות API וכו’, בכתובות URL.
מילות סיכום
מקווה שנהניתם והשכלתם. בפוסט עברנו על דברים שאסור שיקרו, עכשיו אין תירוצים – אל תהיו חלק מאלה שמוסיפים את השגיאות. בנוסף לפני שמבצעים בכלל פיתוח API רצוי לעצור ולתכנן היטב.
חשוב ליישם את השיטות והמוסכמות לבנות יישומים פונקציונליים ביותר שעובדים היטב וכתובים נכון. בסופו של דבר הופכים את חייהם של צרכני ה-API שלך לקלים יותר וגם יקלו עליכם ועל המפתחים שיבואו בעתיד.
מסכימים? ספרו לנו על כך בתגובות
פוסט מעולה שמזקק נושא עמוס תוכן ולכמה בולטים משמעותיים. כל הכבוד.
נחמד. חסרים כמה דברים:
1. לא התייחסת לPATCH
2. אין התייחסות לHATEOAS
3. Sorting, Pagination
4. באימות קלט חשוב: 1. להחזיר את כל השגיאות, ולא רק את הראשונה. 2. להחזיר שגיאות בצורת template שתאפשר לUI לרנדר את החלון בלי להתחיל לפרסר את הטקסט של השגיאה.
5. אפשרות ל”עקוב אחרי” כדי לקבל את האובייקט עם תתי האובייקטים שלו ולא רק לינקים, כדי להמנע מעומס קריאות מיותרות של הclient.
יש עוד אבל אלה המרכזיים.
זה פוסט מצוין, פשוט הוא מתאים לרמה מסוימת – מתחילים+ עד מיד-
פוסט מעולה שמרתי במועדפים
מהממםם טוב לדעת!!
אחלה מאמר חוץ מסעיף 3
קראתי את מספר 3 ולדעתי יש בו הרבה חורים
– היוז קייס לא ברור – האם הכוונה לשרת שמקבל תמונות / מציג תמונות.
– איך איחסון קבצים משותף פותר בעיית סקיילינג?
– ועוד
כאשר יש בעייה שמצריכה סקיילינג – צריך קודם כל להבין מהו צוואר הבקבוק . האם כל פעולה לוקחת יותר מידי זמן? אולי יש עומס בגלל יותר מידי בקשות או כל סיבה אחרת.
לצורך העניין נניח ויש לי שרת rest api עם end point של העלאת תמונות. יכול להיות שבמקום או בנוסף להוספת שרתים שווה להפוך את השרות לאסנכרוני וזה יפתור לי הבעיה.