[Route("book")] public class HomeController : Controller { [Route("index")] public IActionResult Index() { return View(); } [Route("about")] public IActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public IActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
此时,Contact方法就是默认/book路由的Action了,访问/book路径的话,就会显示Contact对应的页面。
规则2:Route和HttpGet可以一起使用,但也很危险
我们前面提到,在Action上即可以使用Route特性,也可以使用HttpGet特性,两者之间的不同,就是多了一个Http Method。很多同学可以要问两个特性在一起使用的时候会有问题么?
其实,这两个特性是可以在一起使用的,示例如下:
[Route("book")] public class HomeController : Controller { [Route("Contact")] [HttpGet("home/Contact2")] public IActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
这样/book/contact和/book/home/contact2这两个网址,都可以访问了。但如果这里定义HttpGet,情况就不一样了,示例如下:
[Route("Contact")] [HttpPost("home/Contact2")]
此时,访问该Action的方式,要么是以GET的方式访问/book/contact地址,要么是以POST的方式访问/book/home/contact2。所以为了避免出错,建议使用的时候不要讲两者混用,即便是要同时支持GET和POST,那也是建议用同类型的HttpXXX来定义这些路由,例如:
[HttpGet("Contact")] [HttpPost("home/Contact2")]
这样,看起来就清晰多了。
规则3:多个Route和多个HttpXXX也可以一起使用,但也很危险
在如下示例中,我们为HomeController定义了2个Route特性,而Contact定义了2个Route特性和1个HttpPost特性。
[Route("book")] [Route("tom")] public class HomeController : Controller { [Route("Contact")] [Route("ContactUS")] [HttpPost("home/Contact2")] public IActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
那么,在上述代码生效后,我们将有六种访问来访问该Action,这六种方式分布如下:
GET:/book/contact GET:/book/contactus GET:/tom/contact GET:/tom/contactus POST:/book/home/contact2 POST:/tom/home/contact2
但是,在视图文件中,通过@Html.ActionLink("Contact", "Contact", "Home")生成链接地址的话,则默认会使用第一个定义的Route,如果要强制指定顺序,则可以使用Order属性来定义排序值,默认会优先使用最小的值。示例如下:
[Route("book", Order = 1)] [Route("tom", Order = 0)] public class HomeController : Controller { [Route("Contact", Order = 1)] [Route("ContactUS", Order = 0)] [HttpPost("home/Contact2", Order = 2)] public IActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); } }
自定义内联参数约束
在前面的介绍中,我们知道任意类型的路由在定义的时候都支持不同的内联参数约束,因为这些约束是基于ASP.NET 5的,而不是基于MVC6的,并且这些约束还是可以扩展的,本节我们就来看看如何自定义一些扩展。
无参数约束
首先,我们来看一个比较简单的约束,即无参数约束,类似于{productId:int}这样的类型约束,假设我们要实现一个AABBCC字符串限定的约束,示例如下:
[Route("index/{productId:aabbcc}")]
为了确保/index/112233和/index/aabbcc是符合约束的,而/index/aabbccdd是不符合约束的,我们首先要自定义一个约束类AABBCCRouteConstraint,并实现IRouteConstraint接口,示例如下:
public class AABBCCRouteConstraint : IRouteConstraint { public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection) { bool b = false; object value; if (values.TryGetValue(routeKey, out value) && value != null) { if (value is string) // 获取传入的值,比如aabbcc或112233 { string aabbcc = value.ToString(); b = !string.IsNullOrWhiteSpace(aabbcc) && aabbcc.Length == 6 && aabbcc[0] == aabbcc[1] && aabbcc[2] == aabbcc[3] && aabbcc[4] == aabbcc[5]; } } return b; } }
在该实现类中,要实现Match方法,根据传入的各种参数,判断是否符合定义的约束,并返回true或false,Match方法的参数中,其中routeKey是约束{productId:aabbcc}对应的参数名称(本例中是productId),values集合中会有该productId所对应的数字(如112233),在该方法通过响应的判断返回true和false。
下一步,就是要将该约束类注册到Routing系统的约束集合中,在Startup.cs的ConfigureServices方法中,执行如下语句:
services.Configure<RouteOptions>(opt => { opt.ConstraintMap.Add("aabbcc", typeof(AABBCCRouteConstraint)); });
注意,这里注册的aabbcc就是前面我们所指定约束名称,完成上述步骤以后,即可实现类似{productId:int}的功能了。
有参数约束