一、前言 1、本文主要內容 ASP.NET Core MVC路由工作原理概述 ASP.NET Core MVC帶路徑參數的路由示例 ASP.NET Core MVC固定前/尾碼的路由示例 ASP.NET Core MVC正則表達式匹配路由示例 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