3.ASP.NET Core MVC终结点路由

1. UseRouting和UseEndpoints方法

1.1作用

路由使用一对由 UseRouting 和 UseEndpoints 注册的中间件:

  • UseRouting 向中间件管道添加路由匹配。此中间件会查看应用中定义的终结点集,并根据请求选择最佳的终结点匹配。
  • UseEndpoints 向中间件管道添加终结点执行。它会运行与所选终结点关联的委托。

1.2显式调用

应用通常不需要显式调用 UseRouting 或 UseEndpoints方法。但是,应用可以通过显式调用UseRouting 和 UseEndpoints方法来更改 UseRouting 和 UseEndpoints 的运行顺序。

app.Use(async (context, next) =>
{
    // ...
    await next(context);
});

app.UseRouting();

app.MapGet("/", () => "Hello World!");

在上述代码中:

  • 对 app.Use 的调用会注册一个在管道的开头运行的自定义中间件。
  • 对 UseRouting 的显式调用将路由匹配中间件配置为在自定义中间件之后运行。
  • 使用 MapGet 注册的终结点在管道末尾运行。

如果前面的示例不包含对 UseRouting 的显式调用,则 app.Use 自定义中间件将在 UseRouting 路由匹配中间件之后运行。

如果找到匹配项,则 显式调用的UseEndpoints 中间件即为终端中间件,否则继续执行后面的中间件。

1.3终结点元数据

前面的示例中有两个终结点,但只有运行状况检查终结点附加了授权策略。如果请求与运行状况检查终结点 /healthz 匹配,则执行授权检查。这表明,终结点可以附加额外的数据。此额外数据称为终结点元数据:

  • 可以通过路由感知中间件来处理元数据。
  • 元数据可以是任意的 .NET 类型。

2.路由模板

2.1路由值

例如 /Products/Details/5,那么路由值为:

{ controller = Products, action = Details, id = 5 }

2.2路由模板

路由参数的格式为{必选路由参数}{可选路由参数?},比如{controller}{action}{id?}

要匹配路由参数分隔符({}),请通过重复该字符来转义分隔符( {{}})。

//单个路径模板
"hello"
//路由参数模板
"{controller}/{action}/{id?}"
//具有默认值的路由参数模板
"{controller=Home}/{action=Index}/{id?}"
//也可以使用defaults参数定义默认值
app.MapControllerRoute("blog_route", "{controller}/{action}/{id?}",
    defaults: new { controller = "Home" });
//将一些参数信息从 URL 传递到控制器
/[Controller]/[ActionName][?Parameter1=value1&Parameter2=value2]
VerbURIAction
[HttpGet]/MoviesIndex
[HttpGet]/Movies/Details/idDetails
[HttpGet]/Movies/CreateCreate
[HttpPost]/Movies/CreateCreate
[HttpGet]/Movies/Edit/idEdit
[HttpPost]/Movies/Edit/idEdit
[HttpGet]/Movies/Delete/idDelete
[HttpPost]/Movies/Delete/idDeleteConfirmed

2.3catch-all路由参数

带星号 * 或双星号 **前缀的路由参数被称为catch-all路由参数。

//假定{ path = "my/path" }
//星号 *
foo/{*path}生成foo/my%2Fpath

//双星号 **
foo/{**path}生成foo/my/path

2.4路由参数内联约束

在路由参数后面添加一个 : 和约束名称可指定路由参数上的内联约束。

[Route("users/{id:int}")]
public User GetUserById(int id) { }

如果约束需要实参,可以在约束名称后添加括号 (...)的形式提供。

blog/{article:minlength(10)}

通过追加另一个 : 和约束名称,可指定多个内联约束。

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { }

请勿将约束用于输入验证。如果约束用于输入验证,则无效的输入将导致 404(找不到页面)响应。路由约束用于消除类似路由的歧义,而不是验证特定路由的输入。

约束示例匹配项示例说明
int{id:int}123456789-123456789匹配任何整数
bool{active:bool}trueFALSE匹配 true 或 false。 不区分大小写
datetime{dob:datetime}2016-12-312016-12-31 7:32pm在固定区域性中匹配有效的 DateTime 值。
decimal{price:decimal}49.99-1,000.01在固定区域性中匹配有效的 decimal 值。
double{weight:double}1.234-1,001.01e8在固定区域性中匹配有效的 double 值。
float{weight:float}1.234-1,001.01e8在固定区域性中匹配有效的 float 值。
guid{id:guid}CD2C1638-1638-72D5-1638-DEADBEEF1638匹配有效的 Guid 值
long{ticks:long}123456789-123456789匹配有效的 long 值
minlength(value){username:minlength(4)}Rick字符串必须至少为 4 个字符
maxlength(value){filename:maxlength(8)}MyFile字符串不得超过 8 个字符
length(length){filename:length(12)}somefile.txt字符串必须正好为 12 个字符
length(min,max){filename:length(8,16)}somefile.txt字符串必须至少为 8 个字符,且不得超过 16 个字符
min(value){age:min(18)}19整数值必须至少为 18
max(value){age:max(120)}91整数值不得超过 120
range(min,max){age:range(18,120)}91整数值必须至少为 18,且不得超过 120
alpha{name:alpha}Rick字符串必须由一个或多个字母字符组成,az,并区分大小写。
regex(expression){ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}123-45-6789字符串必须与正则表达式匹配。 请参阅有关定义正则表达式的提示。
required{name:required}Rick用于强制在 URL 生成过程中存在非参数值

2.5路由参数constraints约束

//也可以使用constraints参数定义约束
app.MapControllerRoute("blog_route", "Manage/{controller}/{action}/{id?}",
        defaults: new { area = "Blog" }, constraints: new { area = "Blog" });

2.6路由参数转换器

ASP.NET Core 框架使用路由参数转化器来转换进行终结点解析的 URI。

 与约束类似,可在路由参数名称后面添加 : 和转换器名称。

slug意为一小段,slugify即为slug的形容词。

app.MapControllerRoute(
    name: "default",
    pattern: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");

当URI为 /subscription-management/get-all时,其中的subscription-management路由参数会被转换为SubscriptionManagementController控制器,get-all路由参数会被转换为GetAll操作,最终URI /subscription-management/get-all将与 SubscriptionManagementController.GetAll 相匹配。

3.传统(Conventional)路由

传统路由支持使用 MapControllerRouteMapAreaControllerRouteMapDefaultControllerRoute终结点。

传统路由放置在Program.cs文件中。

通常将传统路由用于为浏览器处理 HTML 页面的控制器。

传统路由调用依赖于顺序,前面的会覆盖后面的路由,所以应该最具体的放前面,更通用的放后面。

3.1Map终结点

方法描述
Map为指定模式匹配 HTTP 请求。
MapGet为指定模式匹配 HTTP GET 请求。
MapPost为指定模式匹配 HTTP POST 请求。
MapPut为指定模式匹配 HTTP PUT 请求。
MapDelete为指定模式匹配 HTTP DELETE 请求。
MapMethods为指定方法和模式匹配 HTTP 请求。
MapFallback为可能优先级最低的非文件名匹配请求。
app.MapGet("/", () => "Hello World!");
app.MapGet("/hello/{name:alpha}", (string name) => $"Hello {name}!");

3.2MapControllers终结点

方法描述
MapControllers不指定任何路由。
MapControllerRoute使用 namepatterndefaultsconstraints, dataTokens定义路由。
MapDefaultControllerRoute添加默认路由 {controller=Home}/{action=Index}/{id?}
MapAreaControllerRoute使用 nameareaNamepatterndefaultsconstraints, dataTokens定义路由。
MapDynamicControllerRoute尝试使用由TTransformer 生成的路由值选择控制器操作。
MapFallbackToController为可能优先级最低的非文件名匹配请求,请求将被路由到匹配action, 和 controller的控制器终结点。
MapFallbackToAreaController为可能优先级最低的非文件名匹配请求,请求将被路由到匹配actioncontroller, area的控制器终结点。
public static Microsoft.AspNetCore.Builder.ControllerActionEndpointConventionBuilder MapControllerRoute (this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string name, string pattern, object? defaults = default, object? constraints = default, object? dataTokens = default);
//默认(default)传统路由
//按位置传递参数
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

//按命名传递参数
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

//简写
app.MapDefaultControllerRoute();
//专用(dedicated)传统路由
app.MapControllerRoute(
    name: "blog",
    pattern: "blog/{*article}",
    defaults: new { controller = "Blog", action = "Article" });

3.3MapHealthChecks终结点

app.UseAuthentication();
app.UseAuthorization();

app.MapHealthChecks("/healthz").RequireAuthorization();
app.MapGet("/", () => "Hello World!");

MapHealthChecks 调用添加运行状况检查终结点。 将 RequireAuthorization 链接到此调用会将授权策略附加到该终结点。

4.特性(Attribute)路由

特性路由支持使用 MapControllerRouteMapAreaControllerRouteMapControllers终结点。

特性路由支持放置在控制器(controller)或操作(action)上。

通常将特性路由用于处理 REST API 的控制器。

特性路由调用不依赖于顺序,前面的不会覆盖后面的路由,所以路由全部执行,但是最具体的路由有机会在更通用的路由之前执行。

不能通过传统路由访问定义属性路由的操作,反之亦然。

4.1[Route]特性

[Route]特性虽然可用在控制器和操作上,但一般只用在控制器上,用于指定此控制器中的所有操作的路由前缀。

[Route("")]
[Route("Home")]
[Route("Home/Index")]
[Route("Home/Index/{id?}")]

4.2[Http]特性

Http特性:[HttpGet][HttpPost][HttpPut][HttpPatch][HttpDelete][HttpHead]

Http特性只可以用在操作上。

[HttpGet]
[HttpGet("int/{id:int}")]
[HttpGet("/products2/{id}", Name = "Products_List")]

应用于操作的以 / 或 ~/ 开头的路由模板不与应用于控制器的路由模板合并。

[Route("~/")]
[Route("/Home")]
[Route("~/Home/Index")]

4.3标记替换

特性路由支持标记替换,方法是将标记用方括号([])括起来。

标记 [action][area] 和 [controller] 替换为定义了路由的操作中的操作名称值、区域名称值和控制器名称值。

要匹配标记替换分隔符 [ 或 ],可通过重复该字符([[ 或 ]])对其进行转义。

[Route("[controller]/[action]")]
[Route("[controller]/[action]", Name="[controller]_[action]")]

5.区域(Area)路由

5.1通过 Visual Studio 添加区域

在解决方案资源管理器中,右键单击项目名称,然后选择添加新搭建基架的项目MVC 区域,输入区域名称即可。

5.2添加区域路由

public static Microsoft.AspNetCore.Builder.ControllerActionEndpointConventionBuilder MapAreaControllerRoute (this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string name, string areaName, string pattern, object? defaults = default, object? constraints = default, object? dataTokens = default);
//区域路由
//按位置传递参数
app.MapAreaControllerRoute("MyAreaProducts", "Products", "Products/{controller=Home}/{action=Index}/{id?}");

app.MapAreaControllerRoute("MyAreaServices", "Services", "Services/{controller=Home}/{action=Index}/{id?}");

//按命名传递参数
app.MapAreaControllerRoute(
    name: "MyAreaProducts",
    areaName: "Products",
    pattern: "Products/{controller=Home}/{action=Index}/{id?}");

app.MapAreaControllerRoute(
    name: "MyAreaServices",
    areaName: "Services",
    pattern: "Services/{controller=Home}/{action=Index}/{id?}");
//等同于
app.MapControllerRoute("MyAreaProducts", "Products/{controller=Home}/{action=Index}/{id?}",
    defaults: new { area = "Products" }, constraints: new { area = "Products" });

app.MapControllerRoute("MyAreaServices", "Services/{controller=Home}/{action=Index}/{id?}",
    defaults: new { area = "Services" }, constraints: new { area = "Services" });
//如果所有控制器都在同一个区域,则可以使用{area:exists}约束
app.MapControllerRoute(
    name: "blog_route",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

5.3文件夹结构

  • Project name
    • Areas
      • Products
        • Controllers
          • HomeController.cs
          • ManageController.cs
        • Views
          • Home
            • Index.cshtml
          • Manage
            • Index.cshtml
            • About.cshtml
      • Services
        • Controllers
          • HomeController.cs
        • Views
          • Home
            • Index.cshtml

5.4将控制器与区域关联

using Microsoft.AspNetCore.Mvc;
using Microsoft.Docs.Samples;

namespace MVCareas.Areas.Products.Controllers;

[Area("Products")]
public class ManageController : Controller
{
    public IActionResult Index()
    {
        ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo();
        return View();
    }

    public IActionResult About()
    {
        ViewData["routeInfo"] = ControllerContext.MyDisplayRouteInfo();
        return View();
    }
}

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/csharp/aspnetcoremvc/14310.html

(0)
上一篇 2022年5月2日 01:17
下一篇 2022年5月4日 01:19

相关推荐

  • 6.ASP.NET Core MVC标记帮助程序

    1.标记帮助程序 1.1@addTagHelper指令 @addTagHelper 指令指定程序集中要加载的标记帮助程序对于 Views 目录或子目录中的所有视图文件均可用。 @addTagHelper 后第一个参数指定程序集中要加载的的标记帮助程序的范围,第二个参数指定包含标记帮助程序的程序集。 …

  • 4.ASP.NET Core MVC控制器

    右键单击Controllers文件夹-添加–控制器。 单击MVC–控制器-选择合适的脚手架选项–添加-名称复数MoviesController.cs–添加。

  • 5.ASP.NET Core MVC视图

    1.创建视图 右键单击Views文件夹-添加–新建文件夹-名称复数Movies。 右键单击刚刚新建的Views/Movies文件夹-添加–新建项-选择合适的视图选项-名称Index.cshtml–添加。 2.View()帮助方法 2.1视图发现 3.强类型数据 (viewmodel)…

发表评论

登录后才能评论