RPC
RPC(Remote Procedure Call),遠程過程調用),這種RPC形式的API組織形態是類和方法的形式。所以API的請求往往是一個動詞用來標識接口的意思,比如 https://xxxx/GetStudent?id=1 和 https://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接口請求中有三種方式傳遞參數或者數據。
- URL:資源定位,也就是Rest風格,在請求的url中包含信息,比如https://xxxx/Student/1 1就是學生編號。
- QueryString: URL之外的額外信息,比如RPC中https://xxxx/GetStudent?id=1 id=1就是QueryString
- 請求報文體:供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 入門使用及介紹
所以我們在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學生
}
}
當我們修改Rout按照RPC方式,[Route("[controller]/[action]")]
運行後發現swagger展示的接口方式就改變了。並且保留了參數URL的方式。
關於返回狀態碼的問題可以有兩種方式,一種是直接在ControllerBase.Response
響應中指定返回狀態碼。
[HttpDelete("{id}")]
public string DeleteStudent(int id)
{
//刪除id學生
if (id == 1)
{
return "刪除成功";
}
else {
Response.StatusCode = 404;
return "未找到!";
}
}
另外一種方式就是返回泛型ActionResult<string>
,其中OK
和NotFound
是繼承自ActionResult
然後隱式轉換到泛型,也可以直接返回IActionResult或者ActionResult但是類型不確定這樣swagger文檔就不會解析出返回值,所以我們用ActionResult泛型。
[HttpDelete("{id}")]
public ActionResult<string> DeleteStudent(int id)
{
//刪除id學生
if (id == 1)
{
return Ok("刪除成功");
}
else
{
return NotFound("未找到!");
}
}
[HttpDelete("{id}")]
public ActionResult DeleteStudent(int id) //返回ActionResult
{
//刪除id學生
if (id == 1)
{
return Ok("刪除成功");
}
else
{
return NotFound("未找到!");
}
}
最後我們在總結下關於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("未找到!");
}
}
當然也可以使用[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("未找到!");
}
}
最後還有[FromHeader]
、[FromForm]
、[FromBody]
這些獲取參數的方式,不清楚的使用的時候查詢就行了。