1. 程式人生 > >ASP.NET Web API 2 中的特性路由

ASP.NET Web API 2 中的特性路由

導航頁面

上一步

開始

路由是指 Web API 將URI匹配到一個action。 Web API 2 支援一種新的路由方式,叫做“特性路由”。就像它的名字所示,特性路由使用特性來定義路由規則。特性路由可以讓你更好的控制 Web API 的 URI。 比如,你可以很容易的建立指向層次性資源的URI。

早些時候的路由規則,被稱為基於慣例的路由,現在仍被廣泛支援。事實上,你可以在同一個專案中同時使用這兩種技術。

前提

本教程使用VS2017。
當然,也可以使用 NuGet Package Manager(NyGet程式包管理器)來安裝必要的包。“選單”-> “工具” -> “NuGet Package Manager”(Nuget程式包管理器) -> Package Manager Console(程式包管理器控制檯).在PM Console裡面輸入以下命令:

Install-Package Microsoft.AspNet.WebApi.WebHost

為什麼使用特性路由

基於慣例的路由規則的一個優點是,路由模板被定義在單一的地方,並且路由規則適用於所有的控制器。不幸運的是,基於慣例的路由規則很難支援某些在RESTful API中很常見的URI. 例如,資源地址經常包含了子資源地址:顧客有訂單、電影有演員,書有作者之類的。我們建立URI經常需要來反應出這些關係。

/customers/1/orders

對於這樣的URI,使用基於慣例的路由規則是很難創建出來的。儘管可以做,但是如果有非常多的controller和資源型別的話,這樣的路由規則往往很難通配。

使用特性路由,為這樣的URI定義一個路由規則是非常簡單的。只需要在這個controller的action上加這樣一條特性即可:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }  

這裡有一些其他的路由模式,在這些模式下,使用特性路由可以更簡單的做到。

API版本

在這裡例子中, “/api/v1/products”與”/api/v2/products”將會被路由到不同的控制器。

/api/v1/products
/api/v2/products

多引數型

在這裡例子中,“1”是一個訂單號,但是”2013/06/16” 是一個日期。

/orders/1
/orders/2013/06/16

使用特性路由

要使用特性路由,在配置時需要呼叫MapHttpAttributeRoutes 。這個擴充套件方法被定義在System.Web.Http.HttpConfigurationExtensions類裡。

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

特性路由可以與基於慣例的路由一起使用。要想定義基於慣例的路由,需要呼叫MapHttpRoute 方法。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

        // Convention-based routing.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Note: Migrating From Web API 1

在 Web API 2 之前,Web API 專案模板產生的程式碼是這樣的:

protected void Application_Start()
{
    // WARNING - Not compatible with attribute routing.
    WebApiConfig.Register(GlobalConfiguration.Configuration);
}

如果使用了特性路由,這段程式碼將會丟擲一個異常。如果你要在已存在的Web API專案中使用特性路由,要確保將配置程式碼改成下面這個樣子:

protected void Application_Start()
{
    // Pass a delegate to the Configure method.
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

新增路由特性

這裡有一個使用了特性的路由規則的例子:

public class OrdersController : ApiController
{
    [Route("customers/{customerId}/orders")]
    [HttpGet]
    public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}

字串”customers/{customerId}/orders” 是路由的URI模板。Web API 會嘗試將請求URI匹配到這些模板上。在這個例子中,”customers” 和 “orders”是字面量段,並且”{customerId}”是可變的引數。以下URI可以匹配這個模板:

http://localhost/customers/1/orders
http://localhost/customers/bob/orders
http://localhost/customers/1234-5678/orders

你可以使用 constraints 來限制匹配,接下來會講到。

要注意,這個方法中的路由模板中的”{customerId}”引數 與 方法中的customerId引數匹配。當 Web API 呼叫這個controller的這個action時,它會嘗試繫結路由引數。例如,如果URI是 http://example.com/customers/1/orders, Web API 會嘗試將“1”繫結到action的customerId引數。

一個URI模板可以有好幾個引數:

[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }

任何沒有特性路由的控制器方法都會使用基於慣例的路由規則。也就是說,你可以在同一個專案中結合使用這兩種路由規則。

HTTP方法

Web API 也通過請求的HTTP方法來選擇action。預設情況下,Web API 會通過大小寫不敏感的匹配方式來選擇一個以對應的HTTP方法開頭的action。比如,一個controller的方法名為PutCustomers,則它匹配HTTP PUT 請求。

你可以通過使用以下特性來使這樣的慣例規則不再適用。

[HttpDelete]
[HttpGet]
[HttpHead]
[HttpOptions]
[HttpPatch]
[HttpPost]
[HttpPut]

下面的例子中,CreateBook方法匹配HTTP Post 請求

[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }

對於其他的HTTP方法,包括非標準的方法,使用AcceptVerbs特性來包含多個HTTP方法。

// WebDAV method
[Route("api/books")]
[AcceptVerbs("MKCOL")]
public void MakeCollection() { }

路由字首

通常情況下,同一個controller中的路由路徑是有相同字首的,例如:

public class BooksController : ApiController
{
    [Route("api/books")]
    public IEnumerable<Book> GetBooks() { ... }

    [Route("api/books/{id:int}")]
    public Book GetBook(int id) { ... }

    [Route("api/books")]
    [HttpPost]
    public HttpResponseMessage CreateBook(Book book) { ... }
}

你可以使用RoutePrefix特性來修飾一個controller來為它的路由新增公共字首:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET api/books
    [Route("")]
    public IEnumerable<Book> Get() { ... }

    // GET api/books/5
    [Route("{id:int}")]
    public Book Get(int id) { ... }

    // POST api/books
    [Route("")]
    public HttpResponseMessage Post(Book book) { ... }
}

在方法特性中使用波浪線(~)來重寫路由字首:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET /api/authors/1/books
    [Route("~/api/authors/{authorId:int}/books")]
    public IEnumerable<Book> GetByAuthor(int authorId) { ... }

    // ...
}

路由字首可以包含引數:

[RoutePrefix("customers/{customerId}")]
public class OrdersController : ApiController
{
    // GET customers/1/orders
    [Route("orders")]
    public IEnumerable<Order> Get(int customerId) { ... }
}

路由約束

路有約束可以讓你對路由模板中的引數的匹配新增限制。大體語法是”{parameter:constraint}”,例如:

[Route("users/{id:int}"]
public User GetUserById(int id) { ... }

[Route("users/{name}"]
public User GetUserByName(string name) { ... }

這裡,第一個路由僅當URI的id段是整形時才會被選擇。否則,第二個路由將會被選擇。
下面這個表列出了被支援的約束:

##################### 1

主要到有些約束,比如min,是包含括號值的。你可以在一個引數上使用多個約束,使用冒號隔開。

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... }

定製的路由約束

你可以通過實現IHttpRouteConstraint介面來自己定製路由約束。例如,下面的約束限制了引數必須為非零整數。

public class NonZeroConstraint : IHttpRouteConstraint
{
    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            long longValue;
            if (value is long)
            {
                longValue = (long)value;
                return longValue != 0;
            }

            string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
            if (Int64.TryParse(valueString, NumberStyles.Integer, 
                CultureInfo.InvariantCulture, out longValue))
            {
                return longValue != 0;
            }
        }
        return false;
    }
}

下面的程式碼演示瞭如何註冊這個約束:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var constraintResolver = new DefaultInlineConstraintResolver();
        constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));

        config.MapHttpAttributeRoutes(constraintResolver);
    }
}

現在你就可以在你的路由上使用這個約束了:

[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }

你可以通過實現IInlineConstraintResolver 介面來替換整個DefaultInlineConstraintResolver 類。這樣做將會替換掉所有內建的約束,除非你對IInlineConstraintResolver的實現中特地的加上了它們。

可選URI引數以及預設值

你可以通過在路由模板中的引數後面加一個問號來使該引數成為一個可選引數。如果一個路由引數是可選引數,你必須為它指定一個預設值。

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int?}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}

在這個例子中,“/api/books/locale/1033” 和 “/api/books/locale” 將會得到相同的響應。

或者,你也可以在路由模板的引數後面直接宣告預設值,就像下面這樣:

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int=1033}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}

這跟之前的例子幾乎一樣,但是當預設值被使用時,兩者表現還是會有輕微差別:

  1. 對於第一個例子 (“{lcid?}”),預設值1033直接被指定在方法的引數中,所以該引數將會獲取到1033這個準確值。
  2. 對於第二個例子 (“{lcid=1033}”), 預設值1033將會經過一個模型繫結過程。預設的模型繫結器將會將“1033”轉化為數字型1033.然而,你可以在定製的模型繫結器中做一些其他的事情。
    (大多數情況下,除非你在你的管道中寫了自己的模型繫結器之外,這兩種方式是相同的。)

路由名稱

在 Web API 中, 每個路由規則都有一個名稱。路由名稱在產生連結的過程中非常有用處,所以你在HTTP響應中可以包含一個連結。

為了指明路由名稱,要在特性路由中使用Name屬性。下面的例子展示瞭如何設定路由名稱以及如何在產生連結時使用路由名稱。

public class BooksController : ApiController
{
    [Route("api/books/{id}", Name="GetBookById")]
    public BookDto GetBook(int id) 
    {
        // Implementation not shown...
    }

    [Route("api/books")]
    public HttpResponseMessage Post(Book book)
    {
        // Validate and add book to database (not shown)

        var response = Request.CreateResponse(HttpStatusCode.Created);

        // Generate a link to the new book and set the Location header in the response.
        string uri = Url.Link("GetBookById", new { id = book.BookId });
        response.Headers.Location = new Uri(uri);
        return response;
    }
}

路由順序

當framework嘗試將URI匹配到路由規則時,它會使用特定的順序來評價路由。為了指定順序,要在特性路由中設定RouteOrder屬性,該屬性值越小的路由將會越先被評價。該屬性預設值為0。

以下是定義全部順序的步驟:

  1. 在特性路由中比較RouteOrder 的值。
  2. 檢視每個路由模板的URI段。對於每個URI段,按以下規則排序
    a. 字面量
    b. 帶有約束的路由引數
    c. 不帶有約束的路由引數
    d. 帶有約束的萬用字元引數
    e. 不帶有約束的萬用字元引數
  3. 如果有評價結果相等的情況,將會按照路由模板的大小寫不敏感的字串比較來排序。

下面是一個例子。假設你定義了以下controller:

[RoutePrefix("orders")]
public class OrdersController : ApiController
{
    [Route("{id:int}")] // constrained parameter
    public HttpResponseMessage Get(int id) { ... }

    [Route("details")]  // literal
    public HttpResponseMessage GetDetails() { ... }

    [Route("pending", RouteOrder = 1)]
    public HttpResponseMessage GetPending() { ... }

    [Route("{customerName}")]  // unconstrained parameter
    public HttpResponseMessage GetByCustomer(string customerName) { ... }

    [Route("{*date:datetime}")]  // wildcard
    public HttpResponseMessage Get(DateTime date) { ... }
}

這些路由將按照以下排序:

  1. orders/details
  2. orders/{id}
  3. orders/{customerName}
  4. orders/{*date}
  5. orders/pending

注意到“details”是一個出現在”{id}”之前的字面量段,但是”pending” 是在最後的,因為它的RouteOrder 值為1。(這個例子假設沒有客戶名字是”details” 或 “pending”。一般情況下,要避免定義有歧義的路由規則。在這個例子中,對於 GetByCustomer 來說更好的路由模板是”customers/{customerName}” )

下一步

結束

相關推薦

ASP.NET Web API 2 特性路由

導航頁面 上一步 開始 路由是指 Web API 將URI匹配到一個action。 Web API 2 支援一種新的路由方式,叫做“特性路由”。就像它的名字所示,特性路由使用特性來定義路由規則。特性路由可以讓你更好的控制 Web API 的

How ASP.NET Web API 2.0 Works?[持續更新…]

throws case rep 生命 indexof http face auto 攔截 一、概述 RESTful Web API [Web標準篇]RESTful Web API [設計篇] 在一個空ASP.NET Web項目上創建一個ASP.NET Web API 2.

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

1. 導言 路由系統是請求訊息進入ASP.NET Web API訊息處理管道的第一道屏障,其根本目的在於利用註冊的路由對請求的URL進行解析以確定目標HTTPController和Action的名稱,以及與目標Action方法某個引數進行繫結的路由變數。 WebService和WCF的協議都是soap協議

[轉]ASP.NET web API 2 OData enhancements

{0} per yourself res demon services host iss ges 本文轉自:https://www.pluralsight.com/blog/tutorials/asp-net-web-api-2-odata-enhancements Al

ASP.NET Web API 2 OData v4教程

數據 選項 readonly void runtime tca end add serve 程序數據庫格式標準化的開源數據協議 為了增強各種網頁應用程序之間的數據兼容性,微軟公司啟動了一項旨在推廣網頁程序數據庫格式標準化的開源數據協議(OData)計劃,於此同時,他們還發

起航--ASP.NET Web API 2 (C#)

HTTP 是不侷限於網頁的網路協議。同樣,HTTP在對外的服務和資料中,也能夠發揮出強大的作用。因為它簡單,靈活,且普遍被應用。在你所能想到的很多平臺裡,都封裝了HTTP類庫(library),所以HTTP被廣泛應用於客戶端,譬如瀏覽器,手機裝置和傳統的桌面應

Get Started with ASP.NET Web API 2 (C#)

https://docs.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api https://docs.microsoft.com/en-us/

支援Ajax跨域訪問ASP.NET Web Api 2(Cors)的示例

隨著深入使用ASP.NET Web Api,我們可能會在專案中考慮將前端的業務分得更細。比如前端專案使用Angularjs的框架來做UI,而資料則由另一個Web Api 的網站專案來支撐。注意,這裡是兩個Web網站專案了,前端專案主要負責介面的呈現和一些前端的相應業務邏輯處

ASP.NET Web API 2 入門教程

原始碼下載 HTTP不僅提供web頁面服務,在構建公開服務和資料api方面,它也是一個強大的平臺。HTTP簡單、靈活、無處不在。幾乎你能想到的所有的平臺,都有一個HTTP庫,因此HTTP服務可以影響到廣泛的客戶端,包括瀏覽器、移動裝置,和傳統的桌面應用程式

Asp.NET Web API 2系列(一):初識Web API及手動搭建基本框架

 1.導言 隨著Web技術的發展,現在各種框架,前端的,後端的,數不勝數。全棧工程師的壓力越來越大。 PC端,pad端,移動端App(安卓/IOS)的發展,使得前後端一體的開發模式十分笨重。因此,前後端分離是web發展的趨勢,其中,RESTful API是目前前後端分離的最佳實踐,ASP

ASP.NET Web API 2系列(四):基於JWT的token身份認證方案

## 1.引言 通過前邊的系列教程,我們可以掌握WebAPI的初步運用,但是此時的API介面任何人都可以訪問,這顯然不是我們想要的,這時就需要控制對它的訪問,也就是WebAPI的許可權驗證。驗證方式非常多,本文就重點介紹一種常用的驗證方式:基於JWT的token身份認證方案。 ## 2.前期回顧 [Web A

ASP.NET WEB API 特性路由

一、什麼是特性路由? 特性路由是指將RouteAttribute或自定義繼承自RouteAttribute的特性類標記在控制器或ACTION上,同時指定路由Url字串,從而實現路由對映,相比之前的通過Routes.Add或Routes.MapHttpRoute來講,更加靈活與直觀。 若要使用特性路由功能需

(四)Asp.net web api的坑-【api的返回值】

技術分享 要求 data 都是 blog pan odi handle 自己 void無返回值 IHttpActionResult HttpResponseMessage 自定義類型 我這裏並不想贅述這些返回類型, 可以參考博文http://blog.csdn.net/

Web API 2 入門——創建ASP.NET Web API的幫助頁面(谷歌翻譯)

鏈接 所有 action 解決方案 fec amp 開發人員 sharp ima 在這篇文章中 創建API幫助頁面 將幫助頁面添加到現有項目 添加API文檔 在敞篷下 下一步 作者:Mike Wasson 創建Web API時,創建幫助

ASP.NET Web API使用GZIP 或 Deflate壓縮

== too light class using GZip壓縮 public remove log 對於減少響應包的大小和響應速度,壓縮是一種簡單而有效的方式。 那麽如何實現對ASP.NET Web API 進行壓縮呢,我將使用非常流行的庫用於壓縮/解壓縮稱為DotNetZ

ASP.NET Web API編程——路由

完整 sele tail detail col 2-0 text -- 項目 路由過程大致分為三個階段: 1)請求URI匹配已存在路由模板 2)選擇控制器 3)選擇操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定義路由,例如模

ASP.NET Web API對Headers的操作

ASP.NET Web API中對Headers的操作。 1、獲取請求頭資訊 string value = HttpContext.Current.Request.Headers["name"]; 2、新增響應頭資訊 HttpResponseMessage result = ne

ASP.NET Web API 使用 swagger 來管理 API 文件

  本文以 ASP.NET Web API 為後臺框架,利用 EF6 連線 postgreSQL 資料庫,使用 swagger 來生成 REST APIs文件。文章分二個部分,第一部分主要講如何用 EF6 連線 postgreSQL,第二部分是介紹如何整合 swagg

關於ASP.NET Web API 客戶端的請求報文新增 Authorization

public class ReqAuthorizeAttribute:System.Web.Http.AuthorizeAttribute { /// <summary> /// 進行驗證 /// </summary> /

ASP.NET Web Api使用Session、Cache和Application的幾個方法

        在ASP.NET中,Web Api的控制器類派生自ApiControll,該類與ASP.NET的Control類沒有直接關係,因此不能像在ASPX.CS程式碼隱藏類中直接使用HttpContext、HttpApplicationState或HttpSessi