zoukankan      html  css  js  c++  java
  • ASP.NET Web API实践系列04,通过Route等特性设置路由

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

     

    这时候运行项目可能会报如下错误:
    1

     

    这是因为在下载使用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);
    
            }
    
        }
    
    }
    


    在浏览器中输入如下:
    2

     

    如果你使用的是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:WindowsMicrosoft.NetassemblyGAC_MSILSystem.Web.WebPages.Razorv4.0_2.0.0.0__31bf3856ad364e35System.Web.WebPages.Razor.dll”位置处)。类型 B 源自“System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”(在上下文“Default”中的“C:WindowsMicrosoft.NETFrameworkv4.0.30319Temporary ASP.NET Filesvsfeb7ce97a525d58aasse

     

    这是因为,在下载最新版本的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);
    
            }
    
        }
    
    }
    

    3

     

    还可以在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);
    
            }
    
        }
    
    }
    

    4

    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(正则表达式)}

     

    可以为一个参数变量同时设置多个约束:
    [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

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    Java学习二十九天
    Java学习二十八天
    47. Permutations II 全排列可重复版本
    46. Permutations 全排列,无重复
    subset ii 子集 有重复元素
    339. Nested List Weight Sum 339.嵌套列表权重总和
    251. Flatten 2D Vector 平铺二维矩阵
    217. Contains Duplicate数组重复元素
    209. Minimum Size Subarray Sum 结果大于等于目标的最小长度数组
    438. Find All Anagrams in a String 查找字符串中的所有Anagrams
  • 原文地址:https://www.cnblogs.com/darrenji/p/4051716.html
Copyright © 2011-2022 走看看