这篇笔记我们介绍ASP.NET Core的路由系统配置和使用上的一些最佳实践。
ASP.NET Core中,路由系统主要由2个基本概念构成:
Routing中间件:针对用户请求的URL,通过一系列规则匹配到一个终结点
Endpoints(终结点):关联到最终处理用户请求的组件的端点
ASP.NET Core的路由就是通过这两部分组件构成的,下面我们用代码演示Routing中间件和终结点的配置。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/test", async context =>
{
await context.Response.WriteAsync("Hello, ASP.NET Core!");
});
});
app.Run();
代码中,我们首先调用app.UseRouting()
方法向ASP.NET Core的中间件处理管道中注册路由中间件,然后调用app.UseEndpoints()
注册终结点。MapGet()
方法其实就定义了一个最简单的终结点,它匹配一个路径并接收GET请求进行处理。类似MapGet()
,ASP.NET Core还提供了MapPost()
、MapPut()
等方法,除此之外ASP.NET Core还提供了方法来集成其它框架的终结点,例如:
MapRazorPages()
方法MapControllers()
方法MapHub()
方法MapGrpcService()
方法这些都是将各种组件集成到路由系统终结点的方法。实际开发中其实我们极少直接使用前面例子代码的写法来处理HTTP请求,以上代码仅用于演示ASP.NET Core路由系统的基本原理,我们一般都是直接使用这些和框架集成的方法来配置路由。
对于WebAPI和MVC项目,特性路由(常用)和约定路由是两种最常用的路由配置方式。
特性路由是指基于控制器类上标注的Attribute来匹配路由的路由配置方式,我们通过程序启动时在WebApplication
对象上配置MapControllers()
方法可以指定使用这种路由配置,特性路由也是最常用的路由配置方式。
Program.cs
app.MapControllers();
调用MapControllers()
方法后会自动注册程序集中的所有控制器路由。配置好后,我们可以编写类似如下的控制器。
using Microsoft.AspNetCore.Mvc;
namespace DemoWebAPI.Controllers;
[ApiController]
[Route("api/Demo")]
public class DemoController : ControllerBase
{
[HttpGet("DemoAction")]
public string DemoAction()
{
return "Hello, ASP.NET Core!";
}
}
控制器上,起路由配置作用的Attribute是[Route("api/Demo")]
和[HttpGet("DemoAction")]
,该控制器的DemoAction
方法匹配GET /api/Demo/DemoAction
路径,最终匹配的路径就是将类和方法Attribute上的路径配置组合起来。这里路径配置不要使用/
开头,/
代表应用程序根路径,如果你在DemoAction()
方法上标注[HttpGet("/DemoAction")]
,此时该方法匹配的路径就变成GET /DemoAction
了(前面Controller类Attribute的配置被忽略了)。
注意:
1. [HttpGet("DemoAction")]
可以看作[HttpGet][Route("DemoAction")]
的简写。
2. ASP.NET Core中,路由默认不区分大小写,因此我们请求形如/api/demo/demoaction
其实也是可以正常访问的,但实际开发中还是建议前端依照后端控制器中定义的路由准确配置路径,以免未来迁移到其它区分大小写的框架时造成混乱。
约定路由是ASP.NET Core的另一种路由配置方式,我们创建的MVC工程代码中默认使用的就是约定路由,我们通过程序启动时在WebApplication
对象上配置MapControllerRoute()
方法可以指定使用这种路由配置方式。
Program.cs
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
该配置表示默认的约定路由配置是Controller名/Action名/可选的id参数
,Controller名就是控制器类名去掉Controller
字样得到的名字,Action名就是控制器方法名,上面代码还指定了Controller名默认为Home
,Action名默认为Index
,如果我们直接访问/
,那么匹配的控制器就是HomeController
,匹配的方法是Index()
。
在该约定路由配置下,我们可以创建如下控制器。
using Microsoft.AspNetCore.Mvc;
namespace Gacfox.Demo.DemoMVC.Controllers
{
public class DemoController : Controller
{
public string DemoAction()
{
return "Hello, ASP.NET Core!";
}
}
}
该控制器匹配的路径为/Demo/DemoAction
。
注意:实际开发中,约定路由较少使用,这种约定路由的设计其实对代码的编写很不友好,而且暴露了代码中的Controller名、方法名,具有潜在的安全风险,因此建议使用特性(Attribute)路由,这里我们仅作了解。
前面我们使用的都是固定的路由,比如固定匹配/api/Demo/DemoAction
等,实际上ASP.NET Core的路由匹配功能非常强大,这里我们简单介绍一些常见用法。
路由配置中,我们可以使用[controller]
表示控制器名,使用[action]
表示控制器方法名。
[ApiController]
[Route("api/[controller]")]
public class DemoController : ControllerBase
{
[HttpGet]
[Route("[action]")]
public ActionResult DemoAction() {}
}
上面例子中,控制器方法DemoAction()
对应的请求路径是/api/Demo/DemoAction
。
我们可以使用多个[Route("")]
Attribute指定多个路由,下面是一个例子。
using Microsoft.AspNetCore.Mvc;
namespace DemoWebAPI.Controllers;
[ApiController]
[Route("api/Demo")]
public class DemoController : ControllerBase
{
[HttpGet]
[Route("aaa")]
[Route("bbb")]
[Route("ccc")]
public string DemoAction()
{
return "Hello, ASP.NET Core!";
}
}
该控制器会匹配/api/Demo/aaa
、/api/Demo/bbb
、/api/Demo/ccc
三个路径。
我们可以使用大括号{}
匹配并绑定路由中的路径参数,此外还可以给路径参数指定约束:
?
作为参数后缀表示该参数是可选的=
用于设置参数默认值:
用于添加参数类型约束,例如{id:int}
表示参数id
为整型,我们还可以使用:regex(expression)
的形式指定正则约束{a}-{b}
,但参数之间必须有文本或分隔符下面例子代码会匹配例如/api/Demo/DemoAction/1
,但不会匹配/api/Demo/DemoAction/aaa
或/api/Demo/DemoAction
。
[ApiController]
[Route("api/Demo")]
public class DemoController : ControllerBase
{
[HttpGet("DemoAction/{id:int}")]
public string DemoAction(int id)
{
// ...
}
}
我们可以使用*
或**
进行多段匹配,下面是一个例子。
using Microsoft.AspNetCore.Mvc;
namespace DemoWebAPI.Controllers;
[ApiController]
[Route("api/Demo")]
public class DemoController : ControllerBase
{
[HttpGet("DemoAction/{**path}")]
public string DemoAction(string path)
{
// ...
}
}
代码中我们使用了{**path}
指定路由表示这个一个多段匹配,如果我们访问例如/api/Demo/DemoAction/a/b/c
的地址,path
参数的值为a/b/c
。