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

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    Use the Microsoft C++ toolset from the command line
    libeay32.dll 和 ssleay32.dll 在1.1.0之后没有了
    libeay32.dll 1.0.2j crash
    VHD VHDX 区别
    char * 转 wchar *
    C++ 0xC0000094: Integer division by zero
    爬取人人网
    伪造请求头向url传递参数爬取百度默认翻译
    爬取百度贴吧
    urllib爬取实例
  • 原文地址:https://www.cnblogs.com/darrenji/p/4051716.html
Copyright © 2011-2022 走看看