最近因為要把公司的一個 Java Web Api 的 Project,轉換成 ASP. NET Web Api 2 ,所以花了一點時間去研究一下ASP. NET Web Api 2,也特地花了錢去外面拜師求藝,就是希望可以掌握其中的精髓,以下就是對ASP. NET Web Api 2的一點心得及筆記~
一 、了解Rest
- REST(Representational State Transfer) 表現層狀態轉換
- 是Roy Thomas Fielding博士於2000年在他的博士論文中提出
- 為了設計不同系統在網路傳遞資料而生的架構風格
- 是一種風格而不是標準
- 通常基於使用HTTP,URI,和XML以及HTML這些現有的廣泛流行的協定和標準。
二、Restful的特色
- 統一介面(Uniform Interface) :
- 以資源(Resources)為基礎 ,透過唯一的URI存取各個資源
- 透過Json/XML將Request/Response物件表現(Representations)出來
- 無狀態(Stateless) : 所有狀態都記錄在Client端,Server端不會紀錄狀態
- 可快取(Cacheable) :回應的資料可快取,用以改善效率
- 分層設計(Layered System) :不同層級的元件可以分工,提高擴充性
- 客戶伺服器分離模式(Client-Server) :透過單一介面提高Client-Server互動的可見性
三、符合Restful的限制
- 客戶伺服器分離模式(Client-Server)
- 無狀態(Stateless)
- 可快取(Cacheable)
- 統一介面(Uniform Interface)
- 分層設計(Layered System)
只要符合上述6點我們即可以說此Web Api符合Restful 風格設計(Http即是符合Restful的設計)
四、Restful Web Api的設計
- 統一介面(Uniform Interface),來表達所使用資源 Ex : ~/api/books
- 將動作(Action),放在Http Method中 Ex : Get ~/api/books
- 回應狀態放置於Http StatusCode 中 Ex : HTTP/1.1 201 Created
五、常用的Http Method及狀態碼
Function | URI | Http Method | HTTP StatusCode |
新增 | /books | POST | 201,400,401,409,500 |
修改 | /books/{id} | PUT | 204,400,401,404,409,500 |
刪除 | /books/{id} | Delete | 204,400,401,404,409,500 |
查詢 | /books/{id} | Get | 200,400,401,409,500 |
清單 | /books | Get | 200,400,401,409,500 |
一個好的Api設計,是可以從回傳的狀態碼,就可以看出Api成功或失敗以及失敗的原因是什麼,所以定義回傳的狀態碼是很重要的
Http Status Code 參考 : http://www.restapitutorial.com/httpstatuscodes.html
了解基本的Web Api設計風格後我們就可以來實作囉~
Step 1 在Visual Studio新增一個Web Api 2的專案
Step 2 新增一個ApiController => BooksController
Step 3 路由設定,Web Api有兩種路由設定
- 傳統路由:由 WebApiConfig.cs 來定義路由規則與集中管理
- 屬性路由:由Controller來定義Api路由
所以我們可以先打開App_Start\WebApiConfig.cs來看一下,會發現專案原本就預設把兩種路由模式都開啟了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace WebApiDemo { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 設定和服務 // Web API 屬性路由 config.MapHttpAttributeRoutes(); // Web API 傳統路由 config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } } |
基於設計Restful 的彈性清楚的路由結構,會比較建議使用屬性路由來設計
Step 4 於BooksController加入屬性路由設定
1. 在動作方法上加上[Route]屬性規則
1 2 3 4 5 6 |
// GET: api/Books [Route("api/books")] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } |
2. 指定路由參數型別
1 2 3 4 5 6 |
// GET: api/Books/5 [Route("api/books/{cat:int}")] public string GetBooksByCategory(int cat) { return "value"; } |
3.路由參數Default Value
1 2 3 4 5 6 |
// GET: api/Books/5 [Route("api/books/{cat:int=1}")] public string GetBooksByCategory(int cat) { return "value"; } |
1 2 3 4 5 6 |
// GET: api/Books/5 [Route("api/books/{cat:int?}")] public string GetBooksByCategory(int cat=1) { return "value"; } |
以上兩種會有相同的結果
4.指定路由套用順序(預設為0,數字越小表示優先權越高)
- 先比對是否設定路由順序 ( Order ) (預設值為 0) ex : [Route(“api/books”)]
- 純字串、無參數、無限制的路由 (Literal segments) ex : [Route(“api/books”)]
-
有路由參數,且有套用限定詞 ex : [Route(“api/books/{id:int}”)]
- 有路由參數,且無套用限定詞 ex : [Route(“api/books/{id}”)]
- 有萬用路由參數 (Wildcard parameter) 且有套用限定詞 ex : [Route(“{*id:int}”)]
- 有萬用路由參數 (Wildcard parameter) 且無套用限定詞 ex : [Route(“{*id}”)]
1 2 3 4 5 6 |
// GET: api/Books/5 [Route("api/books/{cat:int?} , Order=1")] public string GetBooksByCategory(int cat=1) { return "value"; } |
5.在Controller加上[RoutePrefix]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[RoutePrefix("api/books")] public class BooksController : ApiController { // GET: api/Books [Route("")] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET: api/Books/5 [Route("{cat:int}")] public string GetBooksByCategory(int cat) { return "value"; } // GET: api/Books/5 [Route("{id:int=1}")] public string GetBooksById(int id) { return "value"; } // POST api/others/5/books (用 ~ 可以覆寫 RoutePrefix 設定) [Route("~/api/others/{other:int}/books")] public void Post([FromBody]string value) { } } |
最後我們可以用PostMan實際測試一下
以上就是屬性路由的介紹,接下來要來實作一下也很重要的,如何去定義自己的回應內容~
Step 5 Web Api 的 Action Result,總共會有4大類
- void : 預設回應 HTTP 204 ,不會有內容
- IEnumerable<T> : 任意型別,預設回應 HTTP 200,自動序列化物件到 HTTP 回應主體 (自動判斷 JSON 或 XML 格式)
- IHttpActionResult : 透過呼叫 ExecuteAsync 方法來建立 HttpResponseMessage 物件
- HttpResponseMessage : 此為 Web API 較為底層的回應訊息物件 (可回應任意內容) 只要針對該物件的屬性寫入資料,就可以自動產生 HTTP 回應訊息
1. void => 不會有回傳內容,回傳代碼為204
1 2 3 4 |
// PUT: api/Books/5 public void Put(int id) { } |
用PostMan實際測試
2. IEnumerable<T> => 回傳任意型別,回傳代碼為200
1 2 3 4 5 6 |
// GET: api/Books [Route("")] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } |
用PostMan實際測試
3. IHttpActionResult => 可自定義回傳代碼及內容,較常使用
- 可以直接呼叫System.Web.Http.Results 中的代碼類別生成回傳物件
EX : System.Web.Http.Results.Ok(“Hello!”) => 200
System.Web.Http.Results.BadRequest(“Hello!”) => 400
System.Web.Http.Results.NotFound(“Hello!”) => 404
可參考官網的說明
1 2 3 4 5 6 |
// GET: api/Books/5 [Route("{id:int=1}")] public IHttpActionResult GetBooksById(int id) { return Ok("Success!!!"); } |
用PostMan實際測試
4. HttpResponseMessage => 最底層物件,可回傳任意內容,有兩種建立方式
-
透過 Request.CreateResponse 建立 HttpResponseMessage 物件
1234567891011[Route("CreateBook")][HttpPost]public HttpResponseMessage CreateBook([FromBody]string value){BooksEntity booksEntity = new BooksEntity{BookName = "Create Harry Porter!"};return Request.CreateResponse<BooksEntity>(HttpStatusCode.Created, booksEntity,GlobalConfiguration.Configuration.Formatters.JsonFormatter);}用PostMan實際測試
-
直接建立 HttpResponseMessage 物件
1234567891011121314[Route("UpdateBook")][HttpPost]public HttpResponseMessage UpdateBook([FromBody]string value){BooksEntity booksEntity = new BooksEntity(){BookName = "Update Harry Porter!"};return new HttpResponseMessage(){StatusCode = HttpStatusCode.OK,Content = new ObjectContent<BooksEntity>(booksEntity, GlobalConfiguration.Configuration.Formatters.JsonFormatter)};}用PostMan實際測試
以上就是Web Api設計風格,路由,回傳型態及回傳代碼的簡單介紹,因為也是初學者,所以內容如有錯誤或是需要更精進的地方也歡迎大家指教!