ASP.NET Web API實踐系列04,通過Route等特性設定路由

來源:互聯網
上載者:User

標籤:

ASP.NET Web API路由,簡單來說,就是把用戶端請求映射到對應的Action上的過程。在"ASP.NET Web API實踐系列03,路由模版, 路由慣例, 路由設定"一文中,體驗了通過模版、慣例、HTTP方法來設定路由,這種做法的好處是把路由模版統一放在了App_Start檔案夾下的WebApiConfig類中,方便管理,但缺點是不夠靈活。

 

REST把一切都看成資源,有時候,一個資源連帶子資源,比如Customer和Orders密切關聯,我們可能希望輸入這樣的請求:customers/1/orders,但僅僅憑藉慣例,很難實現這種路由。而實際上,ASP.NET Web API為我們準備了Route特性,該特性可以直接打到Action上,使用非常靈活、直觀。

 

下面就在ASP.NET MVC4下來體驗Route特性的使用方法。

 

允許Route特性

 

首先需要在WebApiConfig中設定。

using System.Web.Http;

namespace MyRoutingAttributes4
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //設定特性路由
            config.MapHttpAttributeRoutes();

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

            // 取消注釋下面的程式碼可對具有 IQueryable 或 IQueryable<T> 傳回型別的操作啟用查詢支援。
            // 若要避免處理意外查詢或惡意查詢,請使用 QueryableAttribute 上的驗證設定來驗證傳入查詢。
            // 有關詳細資料,請訪問 http://go.microsoft.com/fwlink/?LinkId=279712。
            //config.EnableQuerySupport();

            // 若要在應用程式中禁用跟蹤,請注釋掉或刪除以下程式碼
            // 有關詳細資料,請參閱: http://www.asp.net/web-api
            config.EnableSystemDiagnosticsTracing();
        }
    }
}

 

以上的MapHttpAttributeRoutes方法只在ASP.NET Web API較新的版本中才有,如果你的版本比較低,可以通過"NuGet封裝管理員控制台"卸載舊版本,安裝最新版本。

Uninstall-Package microsoft.aspnet.webapi –Force
install-package microsoft.aspnet.webapi

 

接下來,在Global.asax中,需要把原先註冊WebApiConfig的方式注釋掉,採納新的方式,如下:

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            //WebApiConfig.Register(GlobalConfiguration.Configuration);
            //Web API,啟動特性路由
            GlobalConfiguration.Configure(WebApiConfig.Register);

            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

 

這時候運行項目可能會報如下錯誤:

 

這是因為在下載使用ASP.NET Web API最新版本的時候,順帶下載了一個最新版本的icrosoft.AspNet.WebApi.HelpPage。可以把最新版的HelpPage卸載掉,再下載相對老的版本。

Uninstall-Package Microsoft.AspNet.WebApi.HelpPage –Force
Install-Package Microsoft.AspNet.WebApi.HelpPage -Pre

 

使用Route特性

 

建立一個Cusomter類。

namespace MyRoutingAttributes4.Models
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

 

建立一個Order類。

namespace MyRoutingAttributes4.Models
{
    public class Order
    {
        public int Id { get; set; }
        public decimal Total { get; set; }
        public int CustomerId { get; set; }

        public Customer Customer { get; set; }
    }
}

 

建立一個Database類,用來擷取Order集合。

using System.Collections.Generic;
using System.Linq;
using MyRoutingAttributes4.Models;

namespace MyRoutingAttributes4
{
    public class Database
    {
        public static IEnumerable<Order> GetOrdersByCustomerId(int customerId)
        {
            return GetOrders().Where(o => o.CustomerId == customerId);
        }

        private static IEnumerable<Order> GetOrders()
        {
            Customer cus1 = new Customer() { Id = 1, Name = "張三" };
            Customer cus2 = new Customer() { Id = 2, Name = "李四" };

            List<Order> orders = new List<Order>()
            {
                new Order(){Id = 1, Total = 80M, CustomerId = 1, Customer = cus1},
                new Order(){Id = 2, Total = 100M, CustomerId = 1, Customer = cus1},
                new Order(){Id = 3, Total = 120M, CustomerId = 2, Customer = cus2}
            };

            return orders;
        } 
    }
}

 

建立一個空的API控制器,編寫如下:

using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;

namespace MyRoutingAttributes4.Controllers
{
    public class OrdersController : ApiController
    {
        [Route("customers/{customerId}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId)
        {
            return Database.GetOrdersByCustomerId(customerId);
        }
    }
}


在瀏覽器中輸入如下:

 

如果你使用的是ASP.NET MVC4進行開發,在程式第一次啟動並執行時候,可能會報如下錯誤:

[A]System.Web.WebPages.Razor.Configuration.HostSection 無法強制轉換為 [B]System.Web.WebPages.Razor.Configuration.HostSection。類型 A 源自“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”(在上下文“Default”中的“C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Web.WebPages.Razor/v4.0_2.0.0.0__31bf3856ad364e35/System.Web.WebPages.Razor.dll”位置處)。類型 B 源自“System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”(在上下文“Default”中的“C:/Windows/Microsoft.NET/Framework/v4.0.30319/Temporary ASP.NET Files/vs/feb7ce97/a525d58a/asse

 

這是因為,在下載最新版本的ASP.NET Web API的時候,用到了Razor的最新版本。需要在根目錄下的Web.config中作如下配置:

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      ......
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

 

使用RoutePrefix特性 

 

如果想給某個API控制器中的所有Action加上一個首碼,可把RoutePrefix特性打在API控制器上。

比如我們希望是這樣的格式:http://localhost/api/customers/1/orders

 

這樣來修改OrdersController。

using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;

namespace MyRoutingAttributes4.Controllers
{
    [RoutePrefix("api")]
    public class OrdersController : ApiController
    {
        [Route("customers/{customerId}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId)
        {
            return Database.GetOrdersByCustomerId(customerId);
        }
    }
}

 

還可以在Route特性中使用~來重寫Action的首碼規則。

using System.Collections.Generic;
using System.Web.Http;
using MyRoutingAttributes4.Models;

namespace MyRoutingAttributes4.Controllers
{
    [RoutePrefix("api")]
    public class OrdersController : ApiController
    {
        [Route("~/myapi/customers/{customerId:int}/orders")]
        [HttpGet]
        public IEnumerable<Order> FindOrdersByCustomer(int customerId)
        {
            return Database.GetOrdersByCustomerId(customerId);
        }
    }
}

 

RoutePrefix特性定義的首碼還可以帶參數變數:

    [RoutePrefix("api/{customerId}")]
    public class OrdersController : ApiController

 

路由約束

 

可以通過"{參數變數名稱:約束}"來約束路由中的參數變數。

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

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

以上,如果片段變數id為int類型,就路由到第一個Action,如果不是,路由到第二個Action。

 

ASP.NET Web API內建約束包括:

{x:alpha} 約束大小寫英文字母
{x:bool}
{x:datetime}
{x:decimal}
{x:double}
{x:float}
{x:guid}
{x:int}
{x:length(6)}
{x:length(1,20)} 約束長度範圍
{x:long}
{x:maxlength(10)}
{x:min(10)}
{x:range(10,50)}
{x:regex(Regex)}

 

可以為一個參數變數同時設定多個約束:
[Route("api/{id:int:min(1)}")]

 

實現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;
    }
}

 

在App_Start檔案夾中的WebApiConfig中註冊自訂約束。

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}")]

 

選擇性參數及其預設值

 

如果一個路由參數變數是可選的,同時必須給該參數一個預設值。

[Route("api/{id:int?}")]
public IEnumerable<T> Get(int id = 8){}

在約束後面加?,表示可選,在方法參數中給id設定預設值。

 

給路由設定名稱

 

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

 

路由優先順序

 

Route特性設定的路由優先順序是根據慣例和RouteOrder屬性來確定的。

 

慣例是:

1、靜態片段變數
2、帶約束的片段變數
3、不帶約束的片段變數
4、帶約束的萬用字元片段變數
5、不帶約束的萬用字元片段變數

 

RouteOrder屬性的預設值是0,屬性值越小,排在越前面。

 

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

以上,路由的優先順序是:

orders/details  靜態片段變數,RouteOrder屬性值為0
orders/{id} 帶約束的片段變數,RouteOrder屬性值為0
orders/{customerName} 不帶約束的片段變數,RouteOrder屬性值為0
orders/{*date} 帶約束的萬用字元片段變數,RouteOrder屬性值為0
orders/pending RouteOrder屬性值為1

 

 

ASP.NET Web API實踐系列04,通過Route等特性設定路由

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.