Web API-属性路由

路由(Routing)就是Web API如何将一个URI匹配到一个action的过程。Web API 2 支持一个新的路由方式-属性路由(attribute routing)。顾名思义,属性路由使用标识属性去定义路由,属性路由可以使你在Web API中更方便的定制你的URIs。例如,你可以很容易的创建描述不同层次资源的URIs。

前面将的路由风格,叫做基于约定的路由(convention-based)在Web API中也完全支持,实际上,你能够在项目中同时使用两种路由技术。

这篇文章主要演示如何在项目中启用属性路由(attribute routing)和描述各种属性路由的使用方式,主要内容:

--1、为什么需要属性路由

--2、允许属性路由

--3、添加路由属性

--4、路由前缀

--5、路由约束

--6、可选择的URI参数以及默认值

--7、路由名称

--8、路由顺序

1、为什么需要属性路由

第一个发布版的Web API使用 convention-based routing(基于约定的)。在这种风格的路由中,你定义一个或者多个路由模版,基本上是参数化的字符串。当框架接收到一个请求,框架将URI与路由模版进行匹配。

convention-based路由的一个优势是:路由模版定义在一个文件中,路由规则被应用到所以的控制器上。但是convention-based方式的路由风格,要实现支持像RESTful APIs中常见的特定的URI模式比较麻烦。例如,资源经常包含子资源:Customers have orders, movies have actors, books have authors,等等。创建能够反映这种关系的URI是必须的:/customers/1/orders

使用convention-based 路由很难去实现这种风格的URI(This type of URI is difficult to create using convention-based routing),尽管可以实现,但结果不佳,如果你有很多控制器和资源类型。

使用属性路由,你可以很容易的为这样的URI定义一个路由,只需要给一个控制器的action添加一个标识属性:

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

还有一些情况下使用属性路由将非常方便:

--API 版本(API versioning)

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

解释:假设要控制请求访问不同版本的api,如果是convention-based风格的路由,意味着这里的v1 ,v2被定义为参数,那么必须在action中接收这个参数,然后在action中才能判断出版本(貌似这个时候知道版本用处不大了)我们要实现的是v1 ,v2 访问的是不同的控制器。那么使用属性路由很容易实现这一点,比如一个方法只在v2中有:

[Route("/api/v2/products")]
public Ienumerable<Product> GetAll(){}

(如何实现版本控制,细节可能要在项目中去感受)

--重载 URI片段(Overloaded URI segments)

/orders/1
/orders/pending

这个例子中"1"是一个订单编号,但是"pending"对应了一个订单集合。

--复杂的参数类型

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

这个例子中"1" 是一个订单编号,但是"2013/06/16"指定了一个日期。

2、允许属性路由

Global.asax文件

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

WebApiConfig类

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

     // Other Web API configuration not shown.
}

config.MapHttpAttributeRoutes()方法启用属性路由。

3、添加路由标识属性

一个例子:

public class OrdersController : ApiController
{
    [Route("customers/{customerId}/orders")]
    [HttpGet]
    public IEnumerable&lt;Order&gt; FindOrdersByCustomer(int customerId) { ... }
}

字符串"customers/{customerId}/orders"是路由的URI模版,Web API尝试将请求URI与这个模版匹配,这个例子中"coustomers" 和 "orders" 是纯文本片段,{customerId}是占位符,下面的URI都会与这个模版匹配:

  • http://localhost/customers/1/orders

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

可以使用约束限制{customerId}匹配的范围,下面会讲到。

注意在路由模版中的{customerId}参数,和action方法中的customerId参数匹配,当Web API调用控制器的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请求方式

默认情况下Action方法使用方法开头用请求方式名称的方式两匹配不同的HTTP请求(忽略大小写的),可以通过添加标识属性来指定某一个action方法匹配的HTTP请求方式:

[HttpDelete]

[HttpGet]

[HttpHead]

[HttpOptions]

[HttpPatch]

[HttpPost]

[HttpPut]

例如:

[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }// WebDAV method
[Route("api/books")]
[AcceptVerbs("MKCOL")]
public void MakeCollection() { }

4、路由前缀

很多时候一个控制器下的action路由模版的前面部分都是相同的,为了不重复书写,可以这样:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET api/books
    [Route("")]
    public IEnumerable&lt;Book&gt; 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")] 给控制器下的所有action方法增加了路由模版前缀。

如果有特殊情况,你可以在action方法的标识属性中使用浪符号(~)来覆盖指定的统一的路由前缀:

[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) { ... }
}

5、路由约束

路由约束允许你限制路由模版中占位符参数的匹配范围,基本语法是{parameter:constraint}。例如:

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

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

GetUserById 方法只匹配id参数为整型的URI

支持的约束条件:


Constraint


Description


Example


alpha


Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z)


{x:alpha}


bool


Matches a Boolean value.


{x:bool}


datetime


Matches a DateTime value.


{x:datetime}


decimal


Matches a decimal value.


{x:decimal}


double


Matches a 64-bit floating-point value.


{x:double}


float


Matches a 32-bit floating-point value.


{x:float}


guid


Matches a GUID value.


{x:guid}


int


Matches a 32-bit integer value.


{x:int}


length


Matches a string with the specified length or within a specified range of lengths.


{x:length(6)}
{x:length(1,20)}


long


Matches a 64-bit integer value.


{x:long}


max


Matches an integer with a maximum value.


{x:max(10)}


maxlength


Matches a string with a maximum length.


{x:maxlength(10)}


min


Matches an integer with a minimum value.


{x:min(10)}


minlength


Matches a string with a minimum length.


{x:minlength(10)}


range


Matches an integer within a range of values.


{x:range(10,50)}


regex


Matches a regular expression.


{x:regex(^\d{3}-\d{3}-\d{4}$)}

有些约束条件可以组合使用,比如"min":必须为整型且大于或等于1

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

自定义路由约束

通过实现IHttpRouteConstraint接口来创建自定义的路由约束,例如下面的代码定义了一个“不能为0”的整数约束。

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);
    }
}

现在就可以在路由中使用这个自定义的约束条件了:id必须为非0整数

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

还可以通过实现IInlineConstraintResolver 接口的方式来覆盖所有的内置的约束。

6、可选择的URI参数和默认值

你可以通过给路由参数添加 问号"?”的方式来标识这个参数是可选的,如果路由模版中定义了可选参数,那么必须为action方法参数指定一个默认值(可选参数)。

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) { ... }
}

上面两个例子功能基本相同,但也有细微的差别:

--在第一个例子中“{lcid:int?}”,默认值直接在action方法的参数位置指定,所以action方法有一个确定类型的默认值。

--第二个例子中“{lcid=1033}”,因为在路由模版中指定的默认值,那么需要经过模型绑定的过程,模型绑定过程会将“1033”从字符类型转换成数字类型,如何自定义了模型绑定方式,可能还有其他的不同的地方。

两个例子的功能一般情况下都是相同的,除非自定义了模型绑定。

7、路由名称

在Web API中每一个路由项都有一个名称,路由名称在生成链接的时候非常有用,隐藏你可以在返回消息中包含一个有效的链接。

使用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;
    }
}

8、路由顺序

当框架尝试去将一个URI匹配到一个路由时,会给路由进行排序,如果需要自定义顺序,可以在路由标识属性中使用RouteOrder 属性,较小的值排在前面,默认的排序值是0。

排序是如何确定的:

1.比较路由标识属性的RouteOrder属性值。

2.查看路由模版中的每一个URI片段,对于每一个片段,按照下面的方式排序

1-纯文本片段

2-带约束条件的路由参数

3-不带约束条件的路由参数

4-带约束条件的通配符路由参数

5不带约束条件的通配符路由参数

3.In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.

看例子:

[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

前面有讲过,在URI匹配路由模版时是从路由的排列顺序开始匹配,一旦匹配成功则会忽略后面的路由模版了。

时间: 2024-08-29 19:10:06

Web API-属性路由的相关文章

Web Api 的 路由机制

ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台. 所以我们要想学习web api 就要去了解它的路由机制.MVC和web api的路由机制的主要区别是web api 使用的是http 方法而不是url 的路径.本文就演示一下web api 是怎样在controller 里映射 http 请求的. web a

Web API之路由浅谈

Web API的路由,是指明接口地址的方向,是照亮获取数据路上的灯塔,其重要性不言而喻. 本篇文章以vs2015为例,一步步说明路由的创建及使用,其中包括默认路由.自定义路由和特性路由. 我们创建一个新的web api项目,其初始的默认路由是这样的: 不难看出,默认路由的名称(name)是DefaultApi.路由模板(routeTemplate)是api/{controller}{id},api是固定的,{controller}匹配控制器,{id}匹配行为的参数,由defaults可以看出,i

2.4使用属性在 ASP.NET Web API 2 路由创建一个 REST API

Web API 2 支持一种新型的路由,称为属性路由.属性路由的一般概述,请参阅属性路由 Web API 2 中.在本教程中,您将使用属性路由创建一个 REST API 集合的书.API 将支持以下操作 ︰ 行动 URI 的示例 得到的所有书的列表. / api/书 得到一本书的 id. /api/books/1 获得一本书的详细信息. /api/books/1/details 按流派获得书籍的列表. /api/books/fantasy 按出版日期获取书籍的列表. /api/books/dat

ASP.NET Web API编程——路由

路由过程大致分为三个阶段: 1)请求URI匹配已存在路由模板 2)选择控制器 3)选择操作 1匹配已存在的路由模板 路由模板 在WebApiConfig.Register方法中定义路由,例如模板默认生成的路由为: config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Opti

[翻译]ASP.NET Web API的路由

原文:Routing in ASP.NET Web API 在我们新建一个Web API项目时,会在App_Start文件夹下的WebApiConfig.cs中定义一个默认路由: config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); 在默认路由中加

ASP.NET Web API 路由 (上)

1 什什么是ASP.NET Web API 路由 ASP.NET Web路由其实就是一个抽象的消息处理管道,ASP.NET Web API的路由机制不同于ASP.NET的路由机制,但是与ASP.NET的路由有着相似的一套设计. 2 ASP.NET Web API 请求和响应的相关对象 ASP.NET Web API的请求是通过HtppRequestMessage作为管道来处理请求的消息,通过HtppReponseMessage作为管道来处理响应的消息.也就是ASP.NET Web API处理用户

【Web API系列教程】2.1 — ASP.NET Web API中的路由机制

这篇文章描述了ASP.NET Web API如何将HTTP请求发送(路由)到控制器. 备注:如果你对ASP.NET MVC很熟悉,你会发现Web API路由和MVC路由非常相似.主要区别是Web API使用HTTP方法来选择动作(action),而不是URI路径.你也可以在Web API中使用MVC风格的路由.这篇文章不需要ASP.NET MVC的任何知识. 路由表 在ASP.NET Web API中,控制器是一个用于处理HTTP请求的类.控制器中的公共方法被称为动作方法或简单动作.当Web A

Asp.Net Web API 2第六课——Web API路由和动作选择

Asp.Net Web API 导航 Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web API第二课——CRUD操作http://www.cnblogs.com/aehyok/p/3434578.html Asp.Net Web API第三课——.NET客户端调用Web API http://www.cnblogs.com/aehyok/p/3439698.html Asp.Net Web

【Web API系列教程】2.2 — ASP.NET Web API中的路由和动作选择机制

这篇文章描述了ASP.NET Web API如何将HTTP请求路由到控制器上的特定动作. 备注:想要了解关于路由的高层次概述,请查看Routing in ASP.NET Web API. 这篇文章侧重于路由过程的细节.如果你创建了一个Web API项目并且发现一些请求并没有按你预期得到相应的路由,希望这篇文章有所帮助. 路由有以下三个主要阶段: 将URI匹配到路由模板 选择一个控制器 选择一个动作 你可以用自己的习惯行为来替换其中一些过程.在本文中,我会描述默认行为.在结尾,我会指出你可以自定义

Web API 1入门之Self-Host寄宿及路由原理(二)

前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知道Web API它可以快速为HTTP客户端提供API来创建Web服务,为何如此这样说呢?因为我们可以将其作为主机也就是一个服务器来用完全不需要IIS,这就是我们下面要讲的第一个内容Self-Host,实现对Web API寄宿的方式有多种并且都是独立于ASP.NET框架之外,如下Self-Host寄宿