.net 溫故知新【12】:Asp.Net Core WebAPI 中的Rest風格

RPC

RPC(Remote Procedure Call),遠程過程調用),這種RPC形式的API組織形態是類和方法的形式。所以API的請求往往是一個動詞用來標識接口的意思,比如 https://xxxx/GetStudent?id=1https://xxxx/AddStudent 這種風格,並且往往沒有規範需要我們去查看接口定義文檔。HTTP方法基本只用GET和POST,沒有使用HTTP的其它謂詞設計比較簡單。

Rest

Rest:按照Http的語義來使用HTTP協議的一種風格,Rest全稱Representational State Transfer(表現層狀態轉換)。他是一種規範或者設計風格而不是特別的技術。REST形式的API組織形態是資源和實體,請求的路由可以看出對資源的訪問,規範統一接口自解釋。
比如 https://xxxx/Student/1 用Get方法調用就是獲取編號爲1的學生。 https://xxxx/Student/1 用Delete調用就是刪除編號爲1的學生,用delete調用就是刪除該學生。
在HTTP中這些調用方法GET、POST、PUT、PATCH、DELETE 即HTTP謂詞。GET用來獲取資源,POST用來新建資源,PUT用來更新指定資源,PATCH用來批量更新資源,DELETE用來刪除資源,通過謂詞來表示請求動作或者意圖,通過url定位資源。
在請求中GET、PUT、DELETE 請求是冪等的,也就是說可以重試請求。而POST不是冪等,因爲POST意思是添加數據。
在Rest風格中使用狀態碼來標識返回結果,其中常用200、201、400、401、404、410、500等。

ASP.NET Core WebAPI接口

在ASP.NET WebAPI中我們也能看到Rest的風格,理想很豐滿,顯示很骨幹。如果我們嚴格的按照Rest風格設計接口的話,需要對技術人員有很高的要求,需要去劃分不同業務不同的資源定位,而且有些業務也找不到準確的謂詞去定義,響應狀態碼有限無法表達準確的意思,或者是時間上來不及等等原因。
而且這種方式更符合國外語言表達的方式,不太適合我國寶寶體質。
所以我們在設計接口的時候不用非要用Rest風格,我們可以靠近或者在特定的更適合使用Rest接口系統中使用。
本篇我們結合Rest看下接口如何設計和交互。

  • 在http接口請求中有三種方式傳遞參數或者數據。
  1. URL:資源定位,也就是Rest風格,在請求的url中包含信息,比如https://xxxx/Student/1 1就是學生編號。
  2. QueryString: URL之外的額外信息,比如RPC中https://xxxx/GetStudent?id=1 id=1就是QueryString
  3. 請求報文體:供PUT、POST提交提供數據,請求體有多種格式application/x-www-form-urlencoded、multipart/form-data、application/json、text/plain、application/xml。
  • 返回狀態碼
    在RPC中Post請求我們習慣如果請求已經在服務器處理,不管處理結果是否正確,我們都返回200狀態碼。然後在返回數據中用其它信息來標識業務結果。比如{code:1,msg:"成功"}或者{code:0,msg:"失敗"}
    而在Rest 中Post通常用201返回新增成功,delete 刪除的數據不存在返回404,但是404大家知道可能也許是url錯誤,所以表訴不清。
    因此我們在實際設計中可能會進行Rest裁剪,我們既使用RPC的返回結果,同時多用準確的狀態碼,不用什麼都返回200。
    使用RPC風格,儘量使用合理謂詞,不知道使用什麼謂詞的時候就用POST,Get Delete參數儘量用資源定位URL,業務錯誤服務端返回合適的狀體嗎,不知道返回什麼就返回400,如果請求處理成功就用200同時返回結果數據。

在上一篇中遺留的這個問題 .net 溫故知新【11】:Asp.Net Core WebAPI 入門使用及介紹

image

所以我們在Controller中Route配置爲[Controller]則不管方法接口名稱是什麼,仍然以Rest的方式訪問。

    [Route("[controller]")]
    [ApiController]
    public class RestCutController : ControllerBase
    {
        [HttpGet]
        public IEnumerable<string> GetStudents()
        {
            //獲取所有學生
            return new string[] { "student1", "student2" };
        }

        [HttpGet("{id}")]
        public string GetStudent(int id)
        {
            //獲取id的學生
            return "student"+id;
        }

        [HttpPost]
        public void PostStudent([FromBody] string value)
        {
            //新增
        }

        [HttpPut("{id}")]
        public void PutStudent(int id, [FromBody] string value)
        {
            //修改
        }

        [HttpDelete("{id}")]
        public void DeleteStudent(int id)
        {
            //刪除id學生
        }
    }

image

當我們修改Rout按照RPC方式,[Route("[controller]/[action]")] 運行後發現swagger展示的接口方式就改變了。並且保留了參數URL的方式。

image

關於返回狀態碼的問題可以有兩種方式,一種是直接在ControllerBase.Response 響應中指定返回狀態碼。

        [HttpDelete("{id}")]
        public string DeleteStudent(int id)
        {
            //刪除id學生
            if (id == 1)
            {
                return "刪除成功";
            }
            else {

                Response.StatusCode = 404;
                return "未找到!";
            }
        }

另外一種方式就是返回泛型ActionResult<string>,其中OKNotFound是繼承自ActionResult然後隱式轉換到泛型,也可以直接返回IActionResult或者ActionResult但是類型不確定這樣swagger文檔就不會解析出返回值,所以我們用ActionResult泛型。

        [HttpDelete("{id}")]
        public ActionResult<string> DeleteStudent(int id)
        {
            //刪除id學生
            if (id == 1)
            {
                return Ok("刪除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

        [HttpDelete("{id}")]
        public ActionResult DeleteStudent(int id) //返回ActionResult
        {
            //刪除id學生
            if (id == 1)
            {
                return Ok("刪除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

最後我們在總結下關於API參數獲取的方式,在 [HttpGet("{id}")]中我們看到有{id},這個就是佔位符,從RUL中獲取,不光可以配置佔位符還可以配置路徑的其它值,甚至可以隨意組織,只要我們的參數明和佔位符相同就行。

        [HttpDelete("number/{id}/Name/{name}")] //自己組織的URL
        public ActionResult<string> DeleteStudent(int id,string name)
        {
            //刪除id學生
            if (id == 1)
            {
                return Ok("刪除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

當然也可以使用[FromRoute]從route獲取,另外我們還有一些Attribute用於從不同的地方獲取參數,比如從QueryString獲取。那麼我的請求URL就應該是/RestCut/DeleteStudent?id=1

        [HttpDelete]
        public ActionResult<string> DeleteStudent([FromQuery] int id)
        {
            //刪除id學生
            if (id == 1)
            {
                return Ok("刪除成功");
            }
            else
            {
                return NotFound("未找到!");
            }
        }

image

最後還有[FromHeader][FromForm][FromBody]這些獲取參數的方式,不清楚的使用的時候查詢就行了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章