• ASP.NET Core 入门教程 3、ASP.NET Core MVC路由入门


    一、前言

    1、本文主要内容

    • ASP.NET Core MVC路由工作原理概述
    • ASP.NET Core MVC带路径参数的路由示例
    • ASP.NET Core MVC固定前/后缀的路由示例
    • ASP.NET Core MVC正则表达式匹配路由示例
    • ASP.NET Core MVC路由约束与自定义路由约束
    • ASP.NET Core MVC RouteAttribute绑定式路由使用介绍

    2、本教程环境信息

    软件/环境说明
    操作系统 Windows 10
    SDK 2.1.401
    ASP.NET Core 2.1.3
    IDE Visual Studio Code 1.27
    浏览器 Chrome 69

    本篇代码基于上一篇进行调整:https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-02

    3、前置知识

    你可能需要的前置知识

    • MVC框架/模式介绍

    https://baike.baidu.com/item/mvc

    • 正则表达式

    http://www.runoob.com/regexp/regexp-tutorial.html

    二、ASP.NET Core MVC 路由简介

    1、ASP.NET Core MVC路由工作原理概述

    ASP.NET Core MVC路由的作用就是将应用接收到请求转发到对应的控制器去处理。

    应用启动的时候会将路由中间件(RouterMiddleware)加入到请求处理管道中,并将我们配置好的路由加载到路由集合(RouteCollection)中。当应用接收到请求时,会在路由管道(路由中间件)中执行路由匹配,并将请求交给对应的控制器去处理。

    另外,需要特别注意的是,路由的匹配顺序是按照我们定义的顺序从上之下匹配的,遵循是的先配置先生效的原则。

    2、路由配置参数说明

    参数名说明
    name 路由名称,不可重复
    template 路由模板,可在模板中以{name}格式定义路由参数
    defaults 配置路由参数默认值
    constraints 路由约束

    在路由配置中,MVC框架内置了两个参数,controller,action。
    路由匹配通过后,需要根据这两个参数将当前请求交由对应的Controller+Action去处理。所以,这两个参数缺少任何一个,都会导致路由无法正常工作。
    通常我们有两个选择:

    • 在template中指定{controller},{action}参数
    • 在默认值中为controller、action指定默认值

    三、ASP.NET Core MVC 路由示例

    1、准备工作

    为了方便我们进行测试,我们先准备好承接路由的Controller&Action

    • 创建TutorialController

    在Controllers文件夹下新增控制器TutorialController.cs并继承于Controller

    using System;
    using Microsoft.AspNetCore.Mvc;
    
    namespace Ken.Tutorial.Web.Controllers
    {
        public class TutorialController : Controller
        {
          
        }
    }
    
    
    • 增加Action:Index
    public IActionResult Index()
    {
        return Content("ASP.NET Core Tutorial by ken from ken.io");
    }
    
    • 增加Action:Welcome
    public IActionResult Welcome(string name, int age)
    {
        return Content($"Welcome {name}(age:{age}) !");
    }
    

    2、带路径参数的路由

    路由配置:

    routes.MapRoute(
            name: "TutorialPathValueRoute",
            template: "{controller}/{action}/{name}/{age}"
        );
    

    此路由适配URL:

    • /tutorial/welcome/ken/20

    不适配URL:

    • /tutorial/welcome/ken

    如果我们希望不在路径中设置age,也可以被路由到,那么可以将age指定为可选参数,将模板中的{age}修改为{age?}即可

    routes.MapRoute(
            name: "TutorialPathValueRoute",
            template: "{controller}/{action}/{name}/{age?}"
        );
    

    此路由适配URL:

    • /tutorial/welcome/ken/20
    • /tutorial/welcome/ken
    • /tutorial/welcome/ken?age=20

    3、固定前后缀的路由

    固定前缀路由配置:

    routes.MapRoute(
        name: "TutorialPrefixRoute",
        template: "jiaocheng/{action}",
        defaults: new { controller = "Tutorial" }
    );
    

    此路由适配URL:

    • /jiaocheng/index
    • /jiaocheng/welcome

    由于路径参数中不包含controller参数,所以需要在默认值中指定。

    固定后缀路由配置

    routes.MapRoute(
        name: "TutorialSuffixRoute",
        template: "{controller}/{action}.html"
    );
    

    此路由适配URL:

    • /tutorial/index.html
    • /tutorial/welcome.html
    • /home/index.html
    • /home/time.html

    固定后缀的路由适用于伪静态等诉求
    固定前后缀可以根据自己的需求结合起来使用。
    当然,你也可以在路由模板中间设定固定值。

    四、ASP.NET Core MVC 路由约束

    1、路由约束介绍

    路由约束主要是用于约束路由参数,在URL格式满足路有模板要求之后,进行参数检查。如果参数不满足路由约束,那么依然会返回未匹配该路由。最常用的可能就是参数类型校验、参数长度校验、以及通过正则满足的复杂校验。

    在开始之前需要在Startup.cs中引用相关命名空间

    using Microsoft.AspNetCore.Routing;
    using Microsoft.AspNetCore.Routing.Constraints;
    

    2、参数长度约束

    路由配置:约束name长度不能>5

    routes.MapRoute(
        name: "TutorialLengthRoute",
        template: "hello/{name}/{age?}",
        defaults: new { controller = "Tutorial", action = "Welcome", name = "ken" },
        constraints: new { name = new MaxLengthRouteConstraint(5) }
    );
    

    此路由适配

    • /hello
    • /hello/ken
    • /hello/ken/1000

    次路由不适配

    • /hello/kenaaaa

    我们也可以直接在模板中配置路由约束:

    routes.MapRoute(
        name: "TutorialLengthRoute2",
        template: "hello2/{name:maxlength(5)}/{age?}",
        defaults: new { controller = "Tutorial", action = "Welcome", name = "ken" }
    );
    

    3、参数范围约束

    路由配置:约束 1<=age<=150

    routes.MapRoute(
        name: "TutorialLengthRoute",
        template: "hello/{name}/{age?}",
        defaults: new { controller = "Tutorial", action = "Welcome", name = "ken" },
        constraints: new {  age = new CompositeRouteConstraint(new IRouteConstraint[] { 
                                new IntRouteConstraint(), 
                                new MinRouteConstraint(1), 
                                new MaxRouteConstraint(150) }) }
    );
    

    此路由适配:

    • /hello/ken/1
    • /hello/ken/150

    此路由不适配

    • /hello/ken/1000

    我们也可以直接在模板中配置路由约束:

    routes.MapRoute(
        name: "TutorialLengthRoute2",
        template: "hello2/{name}/{age:range(1,150)?}",
        defaults: new { controller = "Tutorial", action = "Welcome", name = "ken" }
    );
    

    4、带有正则表达式约束的路由

    路由配置:

    routes.MapRoute(
        name: "TutorialRegexRoute",
        template: "welcome/{name}",
        defaults: new { controller = "Tutorial", Action = "Welcome" },
        constraints: new { name = @"k[a-z]*" }
    );
    

    此路由适配:

    • /welcome/k
    • /welcome/ken
    • /welcome/kevin

    此路由不适配

    • /welcome/k1
    • /welcome/keN
    • /welcome/tom

    这里我们用正则表达式约束了参数name,必须通过正则k[a-z]*匹配通过,即:以小写字母k开头,且后续可跟0到多个小写字母

    我们也可以直接在模板中配置路由约束:

    routes.MapRoute(
        name: "TutorialRegexRoute2",
        template: "welcome2/{name:regex(@"k[a-z]*")}",
        defaults: new { controller = "Tutorial", Action = "Welcome" }
    );
    

    5、自定义路由约束

    1、创建自定义约束

    在项目根目录创建目录Common,并在目录创建类:NameRouteConstraint.cs,然后实现接口:IRouteConstraint

    using System;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Routing;
    
    namespace Ken.Tutorial.Web.Common
    {
        public class NameRouteConstraint : IRouteConstraint
        {
            public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
            {
                string name = values["name"]?.ToString();
                if (name == null) return true;
                if (name.Length > 5 && name.Contains(",")) return false;
                return true;
            }
        }
    }
    
    

    这里我们约束当name长度>5时,name中不能包含,

    2、路由配置

    引入命名空间

    using Ken.Tutorial.Web.Common;
    

    在ConfigureServices引入路由约束

    public void ConfigureServices(IServiceCollection services)
        {
            //引入MVC模块
            services.AddMvc();
    
            //引入自定义路由约束
            services.Configure<RouteOptions>(options =>
            {
                options.ConstraintMap.Add("name", typeof(NameRouteConstraint));
            });
        }
    

    配置路由

    routes.MapRoute(
        name: "TutorialDiyConstraintRoute",
        template: "diy/{name}",
        defaults: new { controller = "Tutorial", action = "Welcome" },
        constraints: new { name = new NameRouteConstraint() }
    );
    

    此路由适配:

    • /diy/ken
    • /diy/ken,
    • /diy/kenny

    此路由不适配

    • /diy/kenny,

    当然,按照惯例,依然可以在模板中配置路由约束

    routes.MapRoute(
        name: "TutorialDiyConstraintRoute2",
        template: "diy2/{name:name}",
        defaults: new { controller = "Tutorial", action = "Welcome" }
    );
    

    五、ASP.NET Core MVC 绑定式路由配置

    1、路由配置风格

    • 集中式配置

    前面章节提到的路由配置都是在Startup类中进行的集中式路由配置,集中配置的路由,除了template中没有配置{controller}参数,默认都是对所有控制器(Controller)生效的。这种集中配置的方式一般我们只要配置一个默认路由,其他情况我们只需要不满足默认模板的情况下进行配置即可。尤其是对URL没有友好度要求的应用,例如:后台管理系统

    • 分散式配置/绑定式配置

    对于集中式路由配置的方式,如果某个Controller/Action配置了特殊路由,对于代码阅读就会不太友好。不过没关系,ASP.NET Core MVC也提供了RouteAttribute可以让我们在Controller或者Action上直接指定路由模板。

    不过要强调的是,一个控制器只能选择其中一种路由配置,如果控制器标记了RouteAttribute进行路由配置,那么集中式配置的路由将不对其生效。

    2、绑定式路由配置

    在项目Controllers目中新建TestController.cs继承与Controller
    并配置Action与路由

    using System;
    using Microsoft.AspNetCore.Mvc;
    
    namespace Ken.Tutorial.Web.Controllers
    {
        [Route("/test")]
        public class TestController : Controller
        {
            [Route("")]
            [Route("/test/home")]
            public IActionResult Index()
            {
                return Content("ASP.NET Core RouteAttribute test by ken from ken.io");
            }
    
            [Route("servertime")]
            [Route("/t/t")]
            public IActionResult Time(){
                return Content($"ServerTime:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} - ken.io");
            }
        }
    }
    
    
    配置项说明
    [Route("/test")] 表示该Controller访问路由前缀为/test,必须以/开头
    [Route("")] 表示以Controller的路由配置为前缀访问该Action;可以通过/test路由到该Action
    [Route("/test/home")] 表示忽略Controller的路由配置;可以通过/test/home路由到该Action
    [Route("servertime")] 表示以Controller的路由配置为前缀访问该Action;可以通过/test/servertime路由到该Action
    [Route("/t/t")] 表示忽略Controller的路由配置;可以通过/t/t路由到该Action

    RouteAttribute中配置的参数,就相当于我们集中式配置中的路由模板(template),最终框架还是帮我们初始化成路由规则,以[Route("/test/home")]为例,相当于生成了以下路由配置:

    routes.MapRoute(
        name: "Default",
        template: "test/home",
        defaults: new { controller = "Test", action = "Index" }
    );
    

    当然,我们也可以在[Route]配置中使用模板参数,而且依然可以在模板中使用约束,自定义约束也没问题。

    [Route("welcome/{name:name}")]
    public IActionResult Welcome(string name){
        return Content($"Welcome {name} !");
    }
    

    最大的区别就是不能定义默认值了,可能也不需要了,你说是吧。_

    六、备注

    1、附录

    • 本文代码示例

    https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-03

    • 本文参考

    https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/routing?view=aspnetcore-2.1


    本文首发于我的独立博客: https://ken.io/note/asp.net-core-tutorial-mvc-route

  • 相关阅读:
    Grid布局方式
    布局
    应用主题资源
    主题资源
    返回键
    标记 {x:Null},d:DesignWidth,d:DesignHeight
    WMAppManifest.xml
    项目模板
    Panorama和Pivot控件
    ashx页面中context.Session["xxx"]获取不到值的解决办法
  • 原文地址:https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-route.html
Copyright © 2020-2023  润新知