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

1. 導言

路由系統是請求消息進入ASP.NET Web API消息處理管道的第一道屏障,其根本目的在於利用註冊的路由對請求的URL進行解析以肯定目標HTTPController和Action的名稱,以及與目標Action方法某個參數進行綁定的路由變量。html

WebService和WCF的協議都是soap協議,數據的序列化和反序列化都是soap的格式。而WebAPI是基於Http協議,請求和返回格式結果默認是 json格式,所以,比WCF更簡單、更通用,比 WebService 更節省流量、更簡潔。 Web API是在.NET Framework上構建RESTful應用程序的理想平臺,爲了更清楚弄懂WebAPI的路由配置,咱們首先要了解HTTP協議和RESTful架構風格。web

2. HTTP協議

HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是因特網上應用最爲普遍的一種網絡傳輸協議,全部的WWW文件都必須遵照這個標準。咱們在這裏緊列舉和本文關係密切的HTTP請求方法和HTTP狀態碼。數據庫

2.1 HTTP請求方法

根據HTTP標準,HTTP請求可使用多種請求方法。json

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

序號 方法 描述
1 GET 請求指定的頁面信息,並返回實體主體。
2 HEAD 相似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
3 POST 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會致使新的資源的創建和/或已有資源的修改。
4 PUT 從客戶端向服務器傳送的數據取代指定的文檔的內容。
5 DELETE 請求服務器刪除指定的頁面。
6 CONNECT HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。
7 OPTIONS 容許客戶端查看服務器的性能。
8 TRACE 回顯服務器收到的請求,主要用於測試或診斷。

2.2 HTTP狀態碼

當瀏覽者訪問一個網頁時,瀏覽者的瀏覽器會向網頁所在服務器發出請求。當瀏覽器接收並顯示網頁前,此網頁所在的服務器會返回一個包含HTTP狀態碼(HTTP Status Code)的信息頭(server header)用以響應瀏覽器的請求。數組

常見的HTTP狀態碼以下表:瀏覽器

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

3. RESTful介紹

在介紹RESTful以前,咱們需瞭解什麼REST,他有那些特徵,以及REST成熟度模型。緩存

3.1 REST介紹

REST是Representational State Transfer的縮寫,翻譯爲表象化狀態轉變或表述性狀態轉變,是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格,它包含了一個分佈式超文本系統中對於組件、鏈接器和數據的約束。REST 是做爲互聯網自身架構的抽象而出現的,其關鍵在於所定義的架構上的各類約束。只有知足這些約束,才能稱之爲符合 REST 架構風格。安全

3.2 REST系統的特徵

RESR系統包括6個特徵,以下所示:服務器

(1)客戶端-服務器結構(Client-Server)

經過一個統一的接口來分開客戶端和服務器,使得二者能夠獨立開發和演化。客戶端的實現能夠簡化,而服務器能夠更容易的知足可伸縮性的要求;

(2)無狀態(Stateless)

在不一樣的客戶端請求之間,服務器並不保存客戶端相關的上下文狀態信息。任何客戶端發出的每一個請求都包含了服務器處理該請求所需的所有信息;

(3)可緩存(Cachable)

客戶端能夠緩存服務器返回的響應結果。服務器能夠定義響應結果的緩存設置。

(4)分層的系統(Layered System)

在分層的系統中,可能有中間服務器來處理安全策略和緩存等相關問題,以提升系統的可伸縮性。客戶端並不須要瞭解中間的這些層次的細節。

(5)按需代碼(Code-On-Demand,可選)

服務器能夠經過傳輸可執行代碼的方式來擴展或自定義客戶端的行爲。這是一個可選的約束。

(6)統一接口(Uniform Interface)

該約束是 REST 服務的基礎,是客戶端和服務器之間的橋樑。該約束又包含下面 4 個子約束。

  • 資源標識符:每一個資源都有各自的標識符。客戶端在請求時須要指定該標識符。在 REST 服務中,該標識符一般是 URI。客戶端所獲取的是資源的表達(representation),一般使用 XML 或 JSON 格式。
  • 經過資源的表達來操縱資源:客戶端根據所獲得的資源的表達中包含的信息來了解如何操縱資源,好比對資源進行修改或刪除。
  • 自描述的消息:每條消息都包含足夠的信息來描述如何處理該消息。
  • 超媒體做爲應用狀態的引擎(HATEOAS):客戶端經過服務器提供的超媒體內容中動態提供的動做來進行狀態轉換。

3.3 REST成熟度模型

Richardson 提出的 REST 成熟度模型。該模型把 REST 服務按照成熟度劃分紅 4 個層次,咱們經常使用到的就是Level1和Level2,以下圖所:

具體說明以下: 

Level 0:Web 服務只是使用 HTTP 做爲傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。

Level 1:Web 服務引入了資源的概念。每一個資源有對應的標識符和表達。

Level 2:Web 服務使用不一樣的 HTTP 方法來進行不一樣的操做,而且使用 HTTP 狀態碼來表示不一樣的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。

Level 3:Web 服務使用 HATEOAS。在資源的表達中包含了連接信息。客戶端能夠根據連接來發現能夠執行的動做。例如,客戶端經過訂單資源中包含的連接取消某一訂單,GET 請求被髮送去獲取該訂單。HATEOAS 的優勢包括無需在客戶端代碼中寫入硬連接的 URL。此外,因爲資源信息中包含可容許操做的連接,客戶端無需猜想在資源的當前狀態下執行何種操做。

 3.4 RESTful

RESTful是一種常見的REST應用,是遵循REST風格的web服務,REST式的web服務是一種ROA(面向資源的架構)。

RESTful資源操做以下表:

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

注:冪等性:對同一REST接口的屢次訪問,獲得的資源狀態是相同的;安全性:對該REST接口訪問,不會使服務器端資源的狀態發生改變。

RESTful URL請求格式與傳統請求格式比較以下表所示:

傳統URL請求格式 RESTFul請求格式 描述
http:/localhost/user/query/1  GET  http:/localhost/user/1  GET 根據用戶id查詢用戶數據
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默認路由是經過http的方法(get/post/put/delete)去匹配對應的action,也就是說webapi的默認路由並不須要指定action的名稱,固然,WebApi也支持MVC裏面的路由機制,但RestFul風格的服務要求請求的url裏面不能包含action,因此,在WebApi裏面是並不提倡使用MVC路由機制的。下邊經過例子介紹Web API路由原理以及使用。

4.1新建空的Web API應用程序

上篇博客介紹了手動搭建基本框架,此次咱們經過VS2017提供的模板,新建空的WebAPI應用程序MyWebAPI2,構建過程當中僅勾選Web API,以下圖所示。

建立完成,應用程序的目錄結構以下圖所示。

  4.2默認路由

App_Start文件夾下WebApiConfig.cs類用於註冊Web API路由,默認路由代碼以下:

public static void Register(HttpConfiguration config)
        {
           //默認路由
            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,並修改相關代碼,以下所示:

 public class StudentController : ApiController
    {
        private static List<Student> studentList = new List<Student>()
        {
            new Student() {Id = "001", Name = "張三", Sex = "", Age = 19, Dept = "軟件學院"},
            new Student() {Id = "002", Name = "李麗", Sex = "", Age = 19, Dept = "資環學院"}
        };
        
        [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; } }

在實際項目中,增刪改查這些操做都是和數據庫打交道的,這裏爲了演示具體實現,用一個靜態數組存儲數據。運行程序,咱們採用Fiddler工具進行測試調用。

測試一:調用Get()方法

功能說明:經過路由解析StudentController中的Get()Action,獲取全部學生列表。

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

測試結果:HTTP狀態碼爲200,獲取的JSON數據以下所示:

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

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

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

測試結果:HTTP狀態碼爲200,獲取的JSON數據以下所示:

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

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

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

 測試結果:HTTP狀態碼爲200,返回的數據TRUE。重複測試一,獲取全部學生列表以下圖所示。

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

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

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

 測試結果:HTTP狀態碼爲200,返回的數據TRUE。

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

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

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

 測試結果:HTTP狀態碼爲200,返回的數據TRUE。

 總結:經過上邊測試,咱們能夠到URL中沒有用到Action,默認路由就是經過參數和HTTP方法匹配Controller中的Action。

 4.3自定義路由

在實際項目中,若是http請求的類型相同,且請求參數相同(如Get),這個時候按照默認路由確定會出問題,具體出現什麼問題,咱們在這作個測試:

在StudentController中添加一個Action,用於查詢某個學院的全部同窗,代碼以下:

        [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/資環學院;HTTP方法:GET。測試結果發現返回的結果null,跟蹤測試代碼發現該URL調用的Get(string id)這個Action。

那麼問題就來了,怎麼才能調用這個Action呢?其中有兩種方法一種是自定義路由,就像MVC那樣在模板中增長Action,另外一種爲經過Web API路由特性實現。

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

public static void Register(HttpConfiguration config)
        {
           //默認路由
            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/資環學院;HTTP方法:GET。運行結果以下:

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

由此可知經過自定義路由能夠解決該問題,可是不符合RESTful風格,全部不提倡使用該方法。

4.4特性路由

默認路由解決不了的訪問,能夠經過Web API的路由特性解決。

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

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

            //默認路由
            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/軟件學院;HTTP方法:GET。運行結果以下:

經過此例,能夠看到Web API特性路由很是靈活方便,具體使用要結合實際項目靈活運用。

5. 總結

本文在開始部分介紹了HTTP協議及RESTful架構風格,讓咱們更深刻的瞭解Web API的設計初衷,又經過實際的例子(文中的代碼及結果均經過了測試),讓咱們掌握了路由配置的各類方法。文中如有不足之處,還望海涵,博文寫做不易但願多多支持,後續會更新更多內容,感興趣的朋友能夠加關注,歡迎留言交流!

相關文章
相關標籤/搜索