Web API系列(二):靈活多樣的路由配置

1.導(dǎo)言

路由系統(tǒng)是請求消息進入ASP.NET Web API消息處理管道的第一道屏障,其根本目的在于利用注冊的路由對請求的URL進行解析以確定目標(biāo)HTTPController和Action的名稱,以及與目標(biāo)Action方法某個參數(shù)進行綁定的路由變量。

WebService和WCF的協(xié)議都是soap協(xié)議,數(shù)據(jù)的序列化和反序列化都是soap的格式。而WebAPI是基于Http協(xié)議,請求和返回格式結(jié)果默認(rèn)是 json格式,因此,比WCF更簡單、更通用,比 WebService 更節(jié)省流量、更簡潔。 Web API是在.NET Framework上構(gòu)建RESTful應(yīng)用程序的理想平臺,為了更清楚弄懂WebAPI的路由配置,我們首先要了解HTTP協(xié)議和RESTful架構(gòu)風(fēng)格。

2.HTTP協(xié)議

HTTP協(xié)議(HyperText Transfer Protocol,超文本傳輸協(xié)議)是因特網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)傳輸協(xié)議,所有的WWW文件都必須遵守這個標(biāo)準(zhǔn)。我們在這里緊列舉和本文關(guān)系密切的HTTP請求方法和HTTP狀態(tài)碼。

2.1 HTTP請求方法

根據(jù)HTTP標(biāo)準(zhǔn),HTTP請求可以使用多種請求方法。

HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法,HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法,如下表所示:

序號 方法 描述
1 GET 請求指定的頁面信息,并返回實體主體。
2 HEAD 類似于get請求,只不過返回的響應(yīng)中沒有具體的內(nèi)容,用于獲取報頭
3 POST 向指定資源提交數(shù)據(jù)進行處理請求(例如提交表單或者上傳文件)。數(shù)據(jù)被包含在請求體中。POST請求可能會導(dǎo)致新的資源的建立和/或已有資源的修改。
4 PUT 從客戶端向服務(wù)器傳送的數(shù)據(jù)取代指定的文檔的內(nèi)容。
5 DELETE 請求服務(wù)器刪除指定的頁面。
6 CONNECT HTTP/1.1協(xié)議中預(yù)留給能夠?qū)⑦B接改為管道方式的代理服務(wù)器。
7 OPTIONS 允許客戶端查看服務(wù)器的性能。
8 TRACE 回顯服務(wù)器收到的請求,主要用于測試或診斷。

2.2 HTTP狀態(tài)碼

當(dāng)瀏覽者訪問一個網(wǎng)頁時,瀏覽者的瀏覽器會向網(wǎng)頁所在服務(wù)器發(fā)出請求。當(dāng)瀏覽器接收并顯示網(wǎng)頁前,此網(wǎng)頁所在的服務(wù)器會返回一個包含HTTP狀態(tài)碼(HTTP Status Code)的信息頭(server header)用以響應(yīng)瀏覽器的請求。

常見的HTTP狀態(tài)碼如下表:

狀態(tài)碼 狀態(tài)碼英文名稱 中文描述
200 OK 請求成功。一般用于GET與POST請求
301 Moved Permanently 永久移動。請求的資源已被永久的移動到新URI,返回信息會包括新的URI,瀏覽器會自動定向到新URI。今后任何新的請求都應(yīng)使用新的URI代替
404 Not Found 服務(wù)器無法根據(jù)客戶端的請求找到資源(網(wǎng)頁)。通過此代碼,網(wǎng)站設(shè)計人員可設(shè)置"您所請求的資源無法找到"的個性頁面
500 Internal Server Error 服務(wù)器內(nèi)部錯誤,無法完成請求

3.RESTful介紹

在介紹RESTful之前,我們需了解什么REST,他有那些特征,以及REST成熟度模型。

3.1 REST介紹

REST是Representational State Transfer的縮寫,翻譯為表象化狀態(tài)轉(zhuǎn)變或表述性狀態(tài)轉(zhuǎn)變,是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構(gòu)風(fēng)格,它包含了一個分布式超文本系統(tǒng)中對于組件、連接器和數(shù)據(jù)的約束。REST 是作為互聯(lián)網(wǎng)自身架構(gòu)的抽象而出現(xiàn)的,其關(guān)鍵在于所定義的架構(gòu)上的各種約束。只有滿足這些約束,才能稱之為符合 REST 架構(gòu)風(fēng)格。

3.2 REST系統(tǒng)的特征

RESR系統(tǒng)包括6個特征,如下所示:

  • 客戶端-服務(wù)器結(jié)構(gòu)(Client-Server)
    通過一個統(tǒng)一的接口來分開客戶端和服務(wù)器,使得兩者可以獨立開發(fā)和演化。客戶端的實現(xiàn)可以簡化,而服務(wù)器可以更容易的滿足可伸縮性的要求;

  • 無狀態(tài)(Stateless)
    在不同的客戶端請求之間,服務(wù)器并不保存客戶端相關(guān)的上下文狀態(tài)信息。任何客戶端發(fā)出的每個請求都包含了服務(wù)器處理該請求所需的全部信息;

  • 可緩存(Cachable)
    客戶端可以緩存服務(wù)器返回的響應(yīng)結(jié)果。服務(wù)器可以定義響應(yīng)結(jié)果的緩存設(shè)置。

  • 分層的系統(tǒng)(Layered System)
    在分層的系統(tǒng)中,可能有中間服務(wù)器來處理安全策略和緩存等相關(guān)問題,以提高系統(tǒng)的可伸縮性??蛻舳瞬⒉恍枰私庵虚g的這些層次的細(xì)節(jié)。

  • 按需代碼(Code-On-Demand,可選)
    服務(wù)器可以通過傳輸可執(zhí)行代碼的方式來擴展或自定義客戶端的行為。這是一個可選的約束。

  • 統(tǒng)一接口(Uniform Interface)
    該約束是 REST 服務(wù)的基礎(chǔ),是客戶端和服務(wù)器之間的橋梁。該約束又包含下面 4 個子約束。

  • 資源標(biāo)識符:每個資源都有各自的標(biāo)識符??蛻舳嗽谡埱髸r需要指定該標(biāo)識符。在 REST 服務(wù)中,該標(biāo)識符通常是 URI??蛻舳怂@取的是資源的表達(dá)(representation),通常使用 XML 或 JSON 格式。

  • 通過資源的表達(dá)來操縱資源:客戶端根據(jù)所得到的資源的表達(dá)中包含的信息來了解如何操縱資源,比如對資源進行修改或刪除。

  • 自描述的消息:每條消息都包含足夠的信息來描述如何處理該消息。

  • 超媒體作為應(yīng)用狀態(tài)的引擎(HATEOAS):客戶端通過服務(wù)器提供的超媒體內(nèi)容中動態(tài)提供的動作來進行狀態(tài)轉(zhuǎn)換。、

3.3 REST成熟度模型

Richardson 提出的 REST 成熟度模型。該模型把 REST 服務(wù)按照成熟度劃分成 4 個層次,我們常用到的就是Level1和Level2,如下圖所:

具體說明如下:

  • Level 0:Web 服務(wù)只是使用 HTTP 作為傳輸方式,實際上只是遠(yuǎn)程方法調(diào)用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬于此類。

  • Level 1:Web 服務(wù)引入了資源的概念。每個資源有對應(yīng)的標(biāo)識符和表達(dá)。

  • Level 2:Web 服務(wù)使用不同的 HTTP 方法來進行不同的操作,并且使用 HTTP 狀態(tài)碼來表示不同的結(jié)果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。

  • Level 3:Web 服務(wù)使用 HATEOAS。在資源的表達(dá)中包含了鏈接信息??蛻舳丝梢愿鶕?jù)鏈接來發(fā)現(xiàn)可以執(zhí)行的動作。例如,客戶端通過訂單資源中包含的鏈接取消某一訂單,GET 請求被發(fā)送去獲取該訂單。HATEOAS 的優(yōu)點包括無需在客戶端代碼中寫入硬鏈接的 URL。此外,由于資源信息中包含可允許操作的鏈接,客戶端無需猜測在資源的當(dāng)前狀態(tài)下執(zhí)行何種操作。

3.4 RESTful

RESTful是一種常見的REST應(yīng)用,是遵循REST風(fēng)格的web服務(wù),REST式的web服務(wù)是一種ROA(面向資源的架構(gòu))。

RESTful資源操作如下表:

http方法 資源操作 冪等 安全
GET SELECT
POST INSERT
PUT UPDATE
DELETE DELETE

注:冪等性:對同一REST接口的多次訪問,得到的資源狀態(tài)是相同的;安全性:對該REST接口訪問,不會使服務(wù)器端資源的狀態(tài)發(fā)生改變。

RESTful URL請求格式與傳統(tǒng)請求格式比較如下表所示:

傳統(tǒng)URL請求格式 RESTFul請求格式 描述
http:/localhost/user/query/1 GET http:/localhost/user/1 GET 根據(jù)用戶id查詢用戶數(shù)據(jù)
http:/localhost/user/save POST http:/localhost/user POST 新增用戶
http:/localhost/user/update POST http:/localhost/user PUT 修改用戶信息
http:/localhost/user/delete GET/POST http:/localhost/user DELETE 刪除用戶信息

4.Web API路由

路由的目的是用于解析請求的URL來確定Controller和Action。Web API默認(rèn)路由是通過http的方法(get/post/put/delete)去匹配對應(yīng)的action,也就是說webapi的默認(rèn)路由并不需要指定action的名稱,當(dāng)然,WebApi也支持MVC里面的路由機制,但RestFul風(fēng)格的服務(wù)要求請求的url里面不能包含action,所以,在WebApi里面是并不提倡使用MVC路由機制的。下邊通過例子介紹Web API路由原理以及使用。

4.1 新建空的Web API應(yīng)用程序

上篇博客介紹了手動搭建基本框架,這次我們通過VS2017提供的模板,新建空的WebAPI應(yīng)用程序MyWebAPI2,構(gòu)建過程中僅勾選Web API,如下圖所示。

創(chuàng)建完成,應(yīng)用程序的目錄結(jié)構(gòu)如下圖所示。

4.2 默認(rèn)路由

App_Start文件夾下WebApiConfig.cs類用于注冊Web API路由,默認(rèn)路由代碼如下:

public static void Register(HttpConfiguration config)
{
    //默認(rèn)路由
    config.Routes.MapHttpRoute(
       name: "DefaultApi",
       routeTemplate: "api/{controller}/{id}",
       defaults: new { id = RouteParameter.Optional }
    );
}

在Models文件夾增加一個Student類,代碼如下:

public class Student
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Sex { get; set; }
    public int Age { get; set; }
    public string Dept { get; set; }
}

在Controllers文件夾中添加一個Web API控制類(v2.1),命名為StudentController,并修改相關(guān)代碼,如下所示:

public class StudentController : ApiController
{
    private static List<Student> studentList = new List<Student>()
    {
        new Student() {Id = "001", Name = "張三", Sex = "男", Age = 19, Dept = "軟件學(xué)院"},
        new Student() {Id = "002", Name = "李麗", Sex = "女", Age = 19, Dept = "資環(huán)學(xué)院"}
    };
    
    [HttpGet]
    public IEnumerable<Student> Get()
    {
        return studentList;
    }

    [HttpGet]
    public Student Get(string id)
    {
        List<Student> tempList = studentList.Where(p => p.Id == id).ToList();
        return tempList.Count==1?tempList.First():null ;
    }

    [HttpPost]
    public bool Post([FromBody]Student student)
    {
        if (student == null) return false;
        if (studentList.Where(p=>p.Id==student.Id).ToList().Count>0) return false;
        studentList.Add(student);
        return true;
    }

    [HttpPut]
    public bool Put(string id, [FromBody]Student student)
    {
        if (student == null) return false;
        List<Student> tempList = studentList.Where(p => p.Id == id).ToList();
        if (tempList.Count == 0) return false;
        Student originStudent = tempList[0];
        originStudent.Name = student.Name;
        originStudent.Sex = student.Sex;
        originStudent.Age = student.Age;
        originStudent.Dept = student.Dept;
        return true;
    }

    [HttpDelete]
    public bool Delete(string id)
    {
        List<Student> tempList = studentList.Where(p => p.Id == id).ToList();
        if (tempList.Count == 0) return false;
        studentList.Remove(tempList[0]);
        return true;
    }
}

在實際項目中,增刪改查這些操作都是和數(shù)據(jù)庫打交道的,這里為了演示具體實現(xiàn),用一個靜態(tài)數(shù)組存儲數(shù)據(jù)。運行程序,我們采用Fiddler工具進行測試調(diào)用。

測試一:調(diào)用Get()方法

功能說明:通過路由解析StudentController中的Get()Action,獲取所有學(xué)生列表。

URL:http://localhost:52317/api/student;HTTP方法:GET。如下圖所示:

測試結(jié)果:HTTP狀態(tài)碼為200,獲取的JSON數(shù)據(jù)如下所示:

測試二:調(diào)用Get(string id)方法

功能說明:通過路由解析StudentController中的Get(string id)Action,根據(jù)學(xué)號獲取某個學(xué)生信息。

URL:http://localhost:52317/api/student/002;HTTP方法:GET。

測試結(jié)果:HTTP狀態(tài)碼為200,獲取的JSON數(shù)據(jù)如下所示:

測試三:調(diào)用Post([FromBody]Student student)方法

功能說明:通過路由解析StudentController中的Post([FromBody]Student student)Action,插入一條數(shù)據(jù),其中參數(shù)前添加[FromBody]是指該參數(shù)不是從URL中獲取,而是在RequestBody中獲取。

URL:http://localhost:52317/api/student;HTTP方法:POST。在RequestBody輸入Json格式的數(shù)據(jù),如下圖所示:

測試結(jié)果:HTTP狀態(tài)碼為200,返回的數(shù)據(jù)TRUE。重復(fù)測試一,獲取所有學(xué)生列表如下圖所示。

測試四:調(diào)用Put(string id, [FromBody]Student student)方法

功能說明:通過路由解析StudentController中的Put(string id, [FromBody]Student student)Action,實現(xiàn)通過學(xué)號更新信息,其中學(xué)號id是在Url中獲取。

URL:http://localhost:52317/api/student/002;HTTP方法:PUT。

測試結(jié)果:HTTP狀態(tài)碼為200,返回的數(shù)據(jù)TRUE。

測試五:調(diào)用Delete(string id)方法

功能說明:通過路由解析StudentController中的Delete(string id)Action,刪除某學(xué)生信息,其中學(xué)號id是在Url中獲取。

URL:http://localhost:52317/api/student/002;HTTP方法:DELETE

測試結(jié)果:HTTP狀態(tài)碼為200,返回的數(shù)據(jù)TRUE。

總結(jié):通過上邊測試,我們可以到URL中沒有用到Action,默認(rèn)路由就是通過參數(shù)和HTTP方法匹配Controller中的Action。

4.3 自定義路由

在實際項目中,如果http請求的類型相同,且請求參數(shù)相同(如Get),這個時候按照默認(rèn)路由肯定會出問題,具體出現(xiàn)什么問題,我們在這做個測試:

在StudentController中添加一個Action,用于查詢某個學(xué)院的所有同學(xué),代碼如下:

[HttpGet]
public IEnumerable<Student> GetByDept(string id)
{
    List<Student> tempList = studentList.Where(p => p.Dept == id).ToList();
    return tempList;
}

這時,運行程序,輸入URL:http://localhost:52317/api/student/資環(huán)學(xué)院;HTTP方法:GET。測試結(jié)果發(fā)現(xiàn)返回的結(jié)果null,跟蹤測試代碼發(fā)現(xiàn)該URL調(diào)用的Get(string id)這個Action。

那么問題就來了,怎么才能調(diào)用這個Action呢?其中有兩種方法一種是自定義路由,就像MVC那樣在模板中增加Action,另一種為通過Web API路由特性實現(xiàn)。

在App_Start文件夾下WebApiConfig.cs類中添加ActionAPI路由模板,代碼如下:

public static void Register(HttpConfiguration config)
{
    //默認(rèn)路由
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
        //自定義路由
    config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
 }

運行程序,輸入URL:http://localhost:52317/api/student/GetByDept/資環(huán)學(xué)院;HTTP方法:GET。運行結(jié)果如下:

注:若出現(xiàn)中文亂碼,解決途徑請參考Fiddler抓包中文亂碼問題

由此可知通過自定義路由可以解決該問題,但是不符合RESTful風(fēng)格,所有不提倡使用該方法。

4.4 特性路由

默認(rèn)路由解決不了的訪問,可以通過Web API的路由特性解決。

啟動路由特性:在App_Start文件夾下WebApiConfig.cs類中啟用特性路由,代碼如下:

public static void Register(HttpConfiguration config)
{
    //啟用Web API特性路由
    config.MapHttpAttributeRoutes();

        //默認(rèn)路由
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
    );
    //自定義路由
    config.Routes.MapHttpRoute(
            name: "ActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
    );
}

修改StudentController中GetByDept代碼如下:

[Route("student/GetByDept/{dept}")]
[HttpGet]
public IEnumerable<Student> GetByDept(string dept)
{
    List<Student> tempList = studentList.Where(p => p.Dept == dept).ToList();
    return tempList;
}

運行程序,輸入URL:http://localhost:52317/student/GetByDept/軟件學(xué)院;HTTP方法:GET。運行結(jié)果如下:

通過此例,可以看到Web API特性路由非常靈活方便,具體使用要結(jié)合實際項目靈活運用。

5. 總結(jié)

本文在開始部分介紹了HTTP協(xié)議及RESTful架構(gòu)風(fēng)格,讓我們更深入的了解Web API的設(shè)計初衷,又通過實際的例子(文中的代碼及結(jié)果均通過了測試),讓我們掌握了路由配置的各種方法。文中若有不足之處,還望海涵,博文寫作不易希望多多支持,后續(xù)會更新更多內(nèi)容,感興趣的朋友可以加關(guān)注,歡迎留言交流!

掃描添加下方的微信公眾號,獲取更多福利和干貨!也可通公眾號(碼探長)聯(lián)系探長,期待與你相遇!??!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容