WebApi路由

一、MVC和WebApi路由机制比较

1、MVC里面的路由
在MVC里面,默认路由机制是通过url路径去匹配对应的action方法,比如/Home/Index这个url,就表示匹配Home这个Controller下面的Index方法,这个很好理解,因为在MVC里面定义了一个默认路由,在App_Start文件夹下面有一个RouteConfig.cs文件

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }

url: "{controller}/{action}/{id}"这个定义了我们url的规则,{controller}/{action}定义了路由的必须参数,{id}是可选参数
2、WebApi里面的路由
WebApi的默认路由是通过http的方法(get/post/put/delete)去匹配对应的action,也就是说webapi的默认路由并不需要指定action的名称。还是来看看它的默认路由配置,我们新建一个Webapi项目,在App_Start文件夹下面自动生成一个WebApiConfig.cs文件:

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 路由
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }

和MVC类似,routeTemplate: "api/{controller}/{id}"这个定义了路由的模板,api/{controller}是必选参数,{id}是可选参数

那么问题就来了,如果我们的url不包含action的名称,那么如何找到请求的方法呢?

eg:

public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody]string value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }

通过浏览器访问url

//浏览器返回
<ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<string>value1</string>
<string>value2</string>
</ArrayOfstring>

Webapi的路由规则是通过http方法去匹配对应的action,浏览器默认通过url访问的都是get请求,webapi的路由引擎就会去找控制器里面的get请求的方法,由于没有参数,所以自动匹配到了无参数的get请求→Get()方法

++WebApi也支持MVC里面的路由机制,但RestFul风格的服务要求请求的url里面不能包含action,所以,在WebApi里面是并不提倡使用MVC路由机制的++

特性路由

WebApi2默认的路由规则我们称作基于约定路由,很多时候我们使用RESTful风格的URI.简单的路由是没问题的,如 api/Products/{id},但有些事很难处理的,如资源之间存在嵌套关系:客户包含订单,书有作者属性等等。对于这种Uri,我们希望的路由是这样的:/costomers/{customerid}/orders 或 /costomers/{customerid}/orders/{orderid}

通过使用特性路由,我们还可以做API的版本控制

public class ValuesController : ApiController
    {
        [Route("api/Values/{id}/V1")]
        public string Get(int id)
        {
            return $"value {id}";
        }
        [Route("api/Values/{id}/V2")]
        public string GetV2(int id)
        {
            return $"value V2 {id}";
        }
    }
//~/api/Values/1/v1返回
<string>value 1</string>
//~/api/Values/1/v2返回
<string>value V2 1</string>

如果要启用特性路由,需要改成如下代码

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
           //...
           GlobalConfiguration.Configure(WebApiConfig.Register);
           //...
        }
    }

启用特性路由还需要在配置过程中调用System.Web.HttpConfigurationExtensions类的MapHttpAttributeRoutes方法

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

默认情况,WebApi会根据action的方法名前缀查找action(不区分大小写),比如GetUsers,会匹配Get。通过在action上添加HttpMethod特性,可以覆盖action需要映射的Http Method。
可使用的特性包括:[HttpDelete],[HttpPost],[HttpHead],[HttpOptions],[HttpPatch],[HttpGet],[HttpPut]

配置好特性路由,开始使用

    public class DefaultController : ApiController
    {
        [Route("api/Default/{id:int}/{name}")]
        public string GetUser(int id,string name)
        {
            return $"{nameof(GetUser)} id:{id} name:{name}";
        }
        [Route("")]
        [Route("api")]
        [Route("api/Default")]
        public string Get()
        {
            return $"Get value {DateTime.Now}";
        }
    }

在方法上标记Route 即指定方法路由规则,也可以在一个方法上标记多个路由规则

//http://localhost:24727  http://localhost:24727/api http://localhost:24727/api/default  访问的都是DefaultController控制器下的Get()方法
<string>Get value 2020/4/29 14:23:22</string>

//http://localhost:24727/api/default/1/aaa 访问DefaultController控制器下的GetUser()方法
<string>GetUser id:1 name:aaa</string>

路由约束

路由约束让我们可以限制模板参数的匹配方式。一般的语法是 "{参数:约束类型}":

约束 介绍 示例
alpha 匹配大写或小写字母 (a-z, A-Z)
bool
datetime
decimal
double
float 匹配一个 32位浮点数
guid
int
length 匹配一个长度在指定范围内的字符串 {x:length(6)}
long
max 匹配指定了最大值的整数
maxlength 匹配指定了最大长度字符串
min 匹配指定了最小值的整数
minlength 匹配指定了最小长度字符串
range 匹配指定了大小区间的整数
regex 匹配一个正则表达式 {x:regex(^\d{3}-\d{3}-\d{4}$)}
如果要指定多个约束,需要用冒号间隔 [Route("api/Default/{id:int:max(10)}")]
        [HttpGet]
        [Route("api/Default/{id:int:max(10)}")]
        public string MultipleConstraints(int id)
        {
            return $"{nameof(MultipleConstraints)} id:{id} {DateTime.Now}";
        }
//http://localhost:24727/api/default/10 这里的id只能传小于10的整数 否则会报错找不到方法
<string>MultipleConstraints id:10 2020/4/29 14:43:16</string>

在参数约束后面添加一个问号,可以设定URI参数是可选的;

        [HttpGet]
        [Route("api/Default/{id:int?}")]
        public string OptionalParameter(int id=10)
        {
            return $"{nameof(OptionalParameter)} id:{id} {DateTime.Now}";
        }
        //http://localhost:24727/api/default
        <string>OptionalParameter id:10 2020/4/29 14:58:51</string>

也可以像普通方法那样指定默认值:

        [HttpGet]
        [Route("api/Default/{id:int=111}")]
        public string DefaultParameter(int id)
        {
            return $"{nameof(DefaultParameter)} id:{id} {DateTime.Now}";
        } 
        //http://localhost:24727/api/default 
        <string>DefaultParameter id:111 2020/4/29 14:56:52</string>

通常情况下,一个Controller下的action会使用相似的路由模板,这时候可以为整个controller指定[RoutePrefix]特性,以使用共同的前缀

    [RoutePrefix("api/default/{id:int}")]//路由前缀中可以包含参数
    public class DefaultController : ApiController
    {
        [HttpGet]
        [Route("")]//这里的[Route("")]不能省略否则会找不到方法
        public string Prefix(int id)
        {
            return $"{nameof(Prefix)} id:{id} {DateTime.Now}";
        }
        [HttpGet]
        [Route("other")]
        public string NotPrefix(int id)
        {
            return $"{nameof(NotPrefix)} id:{id} {DateTime.Now}";
        }
        [HttpGet]
        [Route("~/api/other/{id:int}")]//如果有某个特殊路由不希望使用前缀,可以在路由中添加~/
        public string Other(int id)
        {
            return $"{nameof(Other)} id:{id} {DateTime.Now}";
        }
        [HttpGet]
        [Route("~/api/Date/{id:int}/{*date:datetime:regex(\\d{4}/\\d{2}/\\d{2})}")]//有时候需要几个路由片段结合起作用,这时候就需要使用字符* 不过这种参数只能用作路由的最后一个参数 
        public string Date(int id,DateTime date)
        {
            return $"{nameof(Date)} id:{id} date:{date} {DateTime.Now}";
        }
    }
//http://localhost:24727/api/default/1
<string>Prefix id:1 2020/4/29 15:55:34</string>
//http://localhost:24727/api/default/1/other
<string>NotPrefix id:1 2020/4/29 15:56:00</string>
//http://localhost:24727/api/other/1
<string>Other id:1 2020/4/29 15:56:25</string>
//http://localhost:24727/api/date/1/2020/04/29
<string>
Date id:1 date:2020/4/29 0:00:00 2020/4/29 15:44:52
</string>

路由顺序

通过设定特性[Route("xxx",RouteOrder=n)]可以指定路由的查找顺序
不过意义不大,通过顺序来控制,还不如设定更好的路由来的实际,而且不至于混乱。

本文参考文档:
posted @ 2020-04-29 17:47  德乌姆列特  阅读(346)  评论(0编辑  收藏  举报