当前位置: 首页 > news >正文

ASP.NET Core 的 Routing

ASP.NET Core 的 Routing

ASP.NET Core 的 controllers 使用Routing 中间件匹配客户端的 url 请求,然后映射到对应的 controller 的处理方法(Action)上。
Actions 可以是 常规路由属性路由 的映射。
MVC App一般使用常规路由。
REST APIs 应该使用属性路由。

MVC App 中用的常规路由

使用 MapControllerRoute() 方法,创建一个单一路由。

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

这个单一路由的名字是"default"。
这个单一路由的模板是"{controller=Home}/{action=Index}/{id?}",Home是默认Controller名称,Index是默认Action名称。

这个路由模板:

  • 匹配的url路径类似:/Products/Details/5
  • 解析的参数值是:{ controller = Products, action = Details, id = 5 }

如果有两个Controller 的定义是这样的:

public class ProductsController : Controller
{public IActionResult Details(int id){// use id;}
}
public class HomeController : Controller
{public IActionResult Index() { ... }
}

如果有 url是:/Products/Details/5,
对应路由模板的 “{controller=Home}/{action=Index}/{id?}”,
因为可以找到Products Controller,所以匹配了ProductsController

  • Products Controller 中的 Products
  • Details 代表Action方法
  • 5 对应入参 id 的值,? 表示 5 是可空的,如果id出现在url中但无值,则默认为0。

而以下 url:都会匹配到HomeController:

  • /Home/Index/17
  • /Home/Index
  • /Home 可以找到Home controllers,没有指定具体的则找默认的 Index action
  • / 没有具体指定controllers和action,则找默认的Home controllers 和 Index action

注意:匹配的过程只基于Controllers名称和Action名称,与代码的命名空间,文件夹层级,Action的参数无关。

设置多个常规路由

可以多次调用 app.MapControllerRoute()。

app.MapControllerRoute(name: "blog",pattern: "blog/{*article}",defaults: new { controller = "Blog", action = "Article" });
app.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");

blog路由在这里是一个专用的常规路由,符合规则的 url 都会映射到BlogController的Article方法。
“/Blog”, “/Blog/Article”, 和 “/Blog/{any-string}” 都会映射到BlogController的Article方法。
blog路由优先于default路由进行匹配,因为它先被注册。

Action 冲突

如果多个Action都被路由系统匹配,那么会:

  • 选择最匹配的,或
  • 抛出异常

比如url /Products/Edit/17 会同时匹配下面的两个 Action

public class ProductsController : Controller
{public IActionResult Edit(int id){return ControllerContext.MyDisplayRouteInfo(id);}[HttpPost]public IActionResult Edit(int id, Product product){return ControllerContext.MyDisplayRouteInfo(id, product.name);}
}

而一般来说,只有id入参的Action,url 一般是GET方法,而入参是int id, Product product的Action,url一般是POST方法。
所以需要使用[HttpGet] 或者 [HttpPOST] 来标记Action,让路由系统匹配最佳的Action,HTTP Method属性包括:

  • [HttpGet]
  • [HttpPost]
  • [HttpPut]
  • [HttpDelete]
  • [HttpHead]
  • [HttpPatch]

REST APIs 中用的属性路由

属性路由没有像传统路由那样的通配路由,而且不再通过Controller和Action的名称匹配,除非使用占位符匹配。
直接在Controller的Action中用属性定义:

public class HomeController : Controller
{[Route("")][Route("Home")][Route("Home/Index")][Route("Home/Index/{id?}")][HttpGet]public IActionResult Index(int? id){return ControllerContext.MyDisplayRouteInfo(id);}[Route("Home/About")][Route("Home/About/{id?}")]public IActionResult About(int? id){return ControllerContext.MyDisplayRouteInfo(id);}
}

意思是一个Action可以匹配多个url模板。
但应该尽量精确的使用url模板,保证效率。

占位符匹配

占位符可以是 [action], [area], 和 [controller],占位符使用area,controller和action的名称进行匹配。
例如:

public class HomeController : Controller
{[Route("")][Route("Home")][Route("[controller]/[action]")]public IActionResult Index(){return ControllerContext.MyDisplayRouteInfo();}[Route("[controller]/[action]")]public IActionResult About(){return ControllerContext.MyDisplayRouteInfo();}
}

[Route("[controller]/[action]")]
public class HomeController : Controller
{[Route("~/")][Route("/Home")][Route("~/Home/Index")]public IActionResult Index(){return ControllerContext.MyDisplayRouteInfo();}public IActionResult About(){return ControllerContext.MyDisplayRouteInfo();}
}

组合匹配

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{[HttpGet]public IActionResult ListProducts(){return ControllerContext.MyDisplayRouteInfo();}[HttpGet("{id}")]public IActionResult GetProduct(int id){return ControllerContext.MyDisplayRouteInfo(id);}
}

在Controller上定义了[Route(“products”)],在Action 上定义了HttpMethod,那么在匹配时,会组合起来匹配,比如:

  • /products 会匹配 ProductsApi.ListProducts
  • /products/5 会匹配 ProductsApi.GetProduct(int)

但是 “/” 或者 “~/” 开头的模板不会被controller的模板组合。

匹配顺序

  • 模板规则越具体的越优先匹配
  • 根据 Order 属性,比如 [Route(“Home”, Order = 2)] 晚于 [Route(“Home”, Order = 1)]

指定参数类型

可以在Url模板中指定参数id的值类型:

public class Products14Controller : Controller
{[HttpPost("product14/{id:int}")]public IActionResult ShowProduct(int id){return ControllerContext.MyDisplayRouteInfo(id);}
}

还可以定义默认值:

public class MyApiControllerAttribute : Attribute, IRouteTemplateProvider
{public string Template => "api/[controller]";public int? Order => 2;public string Name { get; set; } = string.Empty;
}[MyApiController]
[ApiController]
public class MyTestApiController : ControllerBase
{// GET /api/MyTestApi[HttpGet]public IActionResult Get(){return ControllerContext.MyDisplayRouteInfo();}
}

混合使用传统路由和属性路由

Actions 可以是传统路由或者属性路由,但是定义了属性路由的Actions 就不能再被传统路由匹配了,意思是属性路由的优先级更高。
而如果Controller上定义了属性路由,那么所有内部的Actions 就都不能再被传统路由匹配了。

系统保留的路由关键字

保留的路由关键字不应该用在路由匹配系统中,包括:

  • action
  • area
  • controller
  • handler
  • page
    而且在ulr中不可以包含ASCII的"/" 或者" ",路由系统不支持URL 转义。

URL Generation 和 环境值

ASP.NET Core App 可以生成指向Action的 URL,这个功能叫 URL Generation
URL Generation 可以减少 hard code的URL,使代码更健壮,更好维护。
可以在controllers和views使用Url属性使用 IUrlHelper 接口的方法。
先看传统路由的例子:

public class UrlGenerationController : Controller
{public IActionResult Source(){// 生成了url: /UrlGeneration/Destinationvar url = Url.Action("Destination");return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");}public IActionResult Destination(){return ControllerContext.MyDisplayRouteInfo();}
}

这里使用的是传统路由,url 变量的值是URL的路径字符串 “/UrlGeneration/Destination”。通过组合http request的路由值(环境值):

  1. 环境值: { controller = “UrlGeneration”, action = “Source” }
  2. 环境值传给 Url.Action: { controller = “UrlGeneration”, action = “Destination” }
  3. 路由模板: {controller}/{action}/{id?}
  4. 结果: /UrlGeneration/Destination

路由模板中的每个路由参数会被环境值替代。没有值的路由参数要么使用默认值,要么跳过(可空)。

再看属性路由的例子:

public class UrlGenerationAttrController : Controller
{[HttpGet("custom")]public IActionResult Source(){var url = Url.Action("Destination");return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");}[HttpGet("custom/url/to/destination")]public IActionResult Destination(){return ControllerContext.MyDisplayRouteInfo();}
}

Source action 生成了 custom/url/to/destination。

通过Action名称生成URL

Url.Action, LinkGenerator.GetPathByAction 都用于通过Controller和Action的名称生成URL。
使用 Url.Action 时,controller 和 action的路由值由.NET 运行时生成。
假设有一个路由是这样: {a}/{b}/{c}/{d},环境值是 { a = Alice, b = Bob, c = Carol, d = David },那么路由有足够信息生成URL,不需要额外的值。
如果添加了{ d = Donovan },那么{ d = David }会被忽略,会生成URL Alice/Bob/Carol/Donovan。
如果添加了 { c = Cheryl },那么{ c = Carol, d = David } 都会被忽略。

任何不匹配路由参数的额外的值都会被放进query字符串中。比如:

public IActionResult Index()
{var url = Url.Action("Buy", "Products", new { id = 17, color = "red" });return Content(url!);
}

会生成URL: /Products/Buy/17?color=red

下面的代码生成一个绝对路径URL:

public IActionResult Index2()
{var url = Url.Action("Buy", "Products", new { id = 17 }, protocol: Request.Scheme);// Returns https://localhost:5001/Products/Buy/17return Content(url!);
}

通过路由名称生成URL

Url.RouteUrl 可以指定一个路由名称生成URL,而一般不指定Controller或者Action名称。
比如:

public class UrlGeneration2Controller : Controller
{[HttpGet("")]public IActionResult Source(){var url = Url.RouteUrl("Destination_Route");return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");}[HttpGet("custom/url/to/destination2", Name = "Destination_Route")]public IActionResult Destination(){return ControllerContext.MyDisplayRouteInfo();}

下面的代码生成一个指向Destination_Route的链接:

<h1>Test Links</h1>
<ul><li><a href="@Url.RouteUrl("Destination_Route")">Test Destination_Route</a></li>
</ul>

在Action Results中生成URL

在Controlle中最常见的用法是在Action Results中返回一个URL,另一个用法是Redirect到另一个Action。
Controller基类提供了方便的方法来实现Redirect。

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, Customer customer)
{if (ModelState.IsValid){// Update DB with new details.ViewData["Message"] = $"Successful edit of customer {id}";return RedirectToAction("Index");}return View(customer);
}

Area 路由

Area路由是MVC中的功能,可以把相关的功能做成一个分组。
可以通过Routing 命名空间分组controller 和 actions,也可以通过文件夹分组Views。
Areas 可以让多个controller 在不同的Area下有相同的名字。
比如:

app.MapAreaControllerRoute("blog_route", "Blog","Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");
app.Run();

MapAreaControllerRoute创建的路由中,blog_route是路由名,Blog是Area名。
当匹配URL路径比如 /Manage/Users/AddUser 时,“blog_route” 路由生成的路由值是:{ area = Blog, controller = Users, action = AddUser }。
上面这个路由会匹配这个Action:

    [Area("Blog")]public class UsersController : Controller{// GET /manage/users/adduserpublic IActionResult AddUser(){var area = ControllerContext.ActionDescriptor.RouteValues["area"];var actionName = ControllerContext.ActionDescriptor.ActionName;var controllerName = ControllerContext.ActionDescriptor.ControllerName;return Content($"area name:{area}" +$" controller:{controllerName}  action name: {actionName}");}        }

注意:没有Area属性的Controller不会匹配任何带Area的路由模板。

http://www.lryc.cn/news/153770.html

相关文章:

  • IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理
  • RHCE——十一、NFS服务器
  • Python编程练习与解答 练习100:随机密码
  • 华为云云服务器评测 | 从零开始:云耀云服务器L实例的全面使用解析指南
  • 欧科云链研究院探析Facebook稳定币发行经历会不会在PayPal重演
  • docker 容器pip、git安装异常;容器内web对外端口ping不通
  • SpringBoot Mybatis 多数据源 MySQL+Oracle+Redis
  • 【JavaScript 16】对象继承 原型对象属性 原型链 构造函数属性 instanceof运算符 继承 多重继承 模块
  • 地下管线三维自动建模软件MagicPipe3D V3.0发布
  • 百度等8家企业首批上线大模型服务;大语言模型微调之道
  • 二、Mycat2 相关概念及读写分离
  • react利用wangEditor写评论和@功能
  • Android之布局转圆角
  • Linux的目录结构特点
  • 【算法与数据结构】654、LeetCode最大二叉树
  • 您必须尝试的 4 种经典特征提取技术!
  • Unity中Shader的遮罩的实现
  • 架构师成长之路|Redis key过期清除策略
  • ubuntu20.04使用privoxy进行http代理转http代理,并定制http代理头(hide-user-agent的使用方法)
  • 任意文件读取
  • 微信小程序餐饮外卖系统设计与实现
  • 一文速览嵌入式六大出口
  • 华为云云服务器评测 | 宝塔8.0镜像应用
  • 构建简单的Node.js HTTP服务器,发布公网远程访问的快速方法
  • ModaHub魔搭社区:向量数据库产业的现状与技术挑战
  • pmp和软件高项哪个含金量高?
  • 手把手教你用Vite构建第一个Vue3项目
  • 美创科技获通信网络安全服务能力评定(应急响应一级)认证!
  • 计算机视觉与人工智能在医美人脸皮肤诊断方面的应用
  • RCU501 RMP201-8 KONGSBERG 分布式处理单元