RESTful API with ASP.NET Web API 2

סיכום על כתיבת Controller שמגיב לבקשות HTTP.

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

ניצור פרויקט ASP.NET Web Apllication ריק, על מנת שנבין כל שלב בעצמנו.

הבדלים בין Web API 2 לבין MVC Controller

נוסיף לפרויקט תיקייה בשם Controllers (לא חובה דווקא בשם הזה). נוסיף לתיקייה Controller, וייפתח לנו החלון הבא שידרוש מאיתנו לבחור Controller:

Select Controller

אנחנו רואים 2 סוגים של Controllers. אני לא יודע להרחיב לעומק, אבל MVC Controller מתאים יותר למודל MVC שכולל גם View, ומאיתנו לא נדרש View, כי אנחנו רוצים לשלוח נתונים בלבד. אפשר להשתמש ב2 הControllers ל2 המטרות (שליחת נתונים והצגת View), אבל לטובת לימוד של כתיבת RESTful נשתמש בWeb API 2 כדי להבין טוב יותר את הפעולות שאנחנו עושים.

אחרי שנוסיף את Web API Controller - Empty (נבחר ריק- שוב, כדי ללמוד) נכתוב את השם שלו בתיבה שקפצה. השם שלו יהיה מורכב משם הController- הנתונים שנרצה להביא, וסיומת Controller. בדוגמא שלנו נבחר בשם UsersController.

אחרי שלחצנו אישור, Visual Sudio יוסיף כמה קבצים (במידה וזאת הפעם הראשונה שאנחנו מוסיפים Controller).

בקשות HTTP:

בקשות http אלו הבקשות הרגילות שאנחנו משתמשים בהם בגלישה באינטרנט, והם מבוססות על שורת הכתובת. ישנם 4 בקשות http עיקריות:

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

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

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

DELETE- דומה לבקשת GET. מורכבת מכתובת בלבד ומבקשת מהשרת למחוק אובייקט כלשהו.

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

הController:

כמו שראינו, שם הController הוא שם האובייקט שאיתו נתעסק, עם סיומת controller (אצלנו: UsersController), והוא יורש מapiController, בשונה מMVC Controller שיורש מController. מלבד זאת, אין לנו כרגע פונקציות בקובץ.

בתור התחלה, לפני שנצלול לעומק, נוכל להגיד כמה דברים פשוטים, שהם בעצם הברירת מחדל ברגע שיצרנו Controller.

הגישה לController נעשית באמצעות הכתובות:

/api/{Controller} - /api/Users
/api/{Controller}/{id} - /api/Users/5

תגובה לבקשות HTTP:

ונוכל בהתאמה ליצור פונקציות שיגיבו לארבעת בקשות הHTTP. כדי שפונקצייה תגיב לבקשה מסוימת, השם שלה צריך להתחיל בשם הבקשה, לדוגמא: GetAll, PostOne, DeleteByID, Put. הController יודע להתאים את סוג הבקשה לפונקצייה המתאימה.

שליחת נתונים באמצעות תבנית:

כמו שרואים בדוגמאות של הכתובות, ניתן להוסיף לכתובת גם id. את הid הזה נקלוט כמשתנה לפונקצייה, וגם אותו הController יזהה אוטומטית. לדוגמא:

[code language="csharp"] public string GetAll(){} //path: /api/Users public string GetByID(int id){} //path: /api/Users/3, and id=3 [/code]

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

שליחת נתונים בעזרת מחרוזת שאילתא:

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

https://www.google.com?search=HelloWorld&type=photos

המחרוזת הזאת תיתן לנו ערכים למשתנים search, type. בתאמה, נוכל לכתוב פונקציה שמבקשת כמה ערכים, ואם תהיה כתובת שנותנת ערכים כאלה, הפונקצייה תיקרא.

[code language="csharp"] public GetByName(string name){} // path: /api/Users?name=Baruch public Get(int id, string name){} // path: /api/Users/5?name=Baruch [/code]

במידה והController לא מוצא פונקצייה שמתאימה בדיוק לערכים שהוא קיבל, הוא ישתמש בפונקציית ברירת המחדל (ללא ערכים).

אני לא מרחיב יותר מידי, אפשר על פי הדברים האלו לעשות ניסויים- מה קורה אם משנים את סדר המשתנים? מה קורה אם שולחים את id כמחרוזת שאילתא? מה קורה אם בכתובת כותבים את id כstring?

ניתוב (Routing)

עד עכשיו השתמשנו בכתובות ובערכי ברירת המחדל. עכשיו נראה איך אפשר לשנות את הכתובת שאליה הController מגיב.

שלבי הניתוב שאנחנו מכירים:

  1. הASP.NET לוקח מהכתובת של הבקשה את חלק ה{Controller}, מצמיד אותו למילה Controller ומחפש Controller בשם הזה.
  2. בתוך הController מחפש פונקצייה שמתחילה בשם הבקשה.
  3. לאחר שנמצאו הפונקציות המתאימות לבקשה, מנסה להתאים את רשימת המשתנים לפונקצייה.

נראה שאת חלק מהשלבים אפשר לשנות.

הניתוב  מתבצע ע"י טבלת ניתוב שמוגדרת בקובץ WebApiConfig.cs שנמצא בתיקיית App_Start, ונוצר אוטומטית בזמן שהוספנו את הController הראשון.

[code language="csharp"] config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); [/code]

זה הקוד. נעבור על שלושת הערכים שהוא מקבל:

  1. name: שם הניתוב, לא יודע עד כמה הוא משמעותי.
  2. routeTemplate: מגדיר את מבנה הכתובת. {Controller} הוא שומר מקום לשם הController, ובהמשך נראה שומרי מקום נוספים. שאר שומרי המקום הם לבחירתנו, והם השמות של המשתנים. גם עם זה ניתן לשחק ולראות מה קורה.
  3. default: כאן ניתן להגדיר האם יש פרמטרים שניתן להתעלם מהם. אם לא היינו מגדירים את id כאופציונלי, הכתובת ללא id לא הייתה חוקית, ולא הייתה מתקבלת.

שינויים אפשריים בכתיבת הפונקציות:

  • ניתן לכתוב פונקצייה ללא התחלה של שם הבקשה, ולשייך אותה בעזרת אפיון לפי הדוגמא:
  • [code language="csharp"] [HttpGet] public string GiveMe(int id){} [/code]

     

  • ניתן להוסיף פרמטר נוסף לכתובת הניתוב- {action}, וניתן להשתמש בו ב2 דרכים:
    • הaction בשורת הכתובת יפנה אותנו לשימוש בפונקצייה בעלת אותו שם: [code language="csharp"] [HttpGet] public string Members(int id) //path: /api/{Controler}/{action}/{id} [/code]
    • ניתן להכריז על פונקצייה כמגיבה לפעולה זאת: [code language="csharp"][/code] [HttpGet] [ActionName("Members")] public string GiveMe() [code language="csharp"][/code]

זהו. זה היה הבסיס. במאמר הבא (אני מקווה) תהיה שיטה טובה יותר לניתוב, ובהמשך נדבר גם על יצירת בקשות וקבלת נתונים, ובסוף על בניית מאגר נתונים.