【ASP.NET Core】MVC控制器的各种自定义:特性化的路由规则
MVC的路由规则配置方式比较多,咱们用得最多的是两种:
A、全局规则。就是我们熟悉的”{controller}/{action}“。
app.MapControllerRoute(
name: "bug",
pattern: "{controller}/{action}"
);
app.MapControllerRoute(
name: "八阿哥",
pattern: "app/{action}",
defaults: new
{
controller = "Home"
}
);
其中,controller、action、area、page 这些字段名用于专属匹配。比如 controller 匹配控制器名称等。这个老周不必多说了,大伙伴们都知道。大括号({ })括起来的字段是全局路由。这些路由可以用于当前应用中所有未指定特性化路由的控制器。上面代码中第二条路由,由于URL模板缺少了 controller 字段,所以 defaults 参数要设定它调用的控制器是 Home。
B、特性化路由(局部路由)。此规则通过 [Route]、[HttpGet]、[HttpPost] 等特性类,在控制器类或方法上配置的路由规则。
[Route("abc")]
public class PigController:ControllerBase
{
[Route("xyz")]
public IActionResult Greeting()
{
return Content("来自猪的问候");
}
}
这样的规则会进行合并。即控制器上的是”abc“,方法上是”xyz“,所以你要调用Greeting方法就要访问URL:
http://www.xxx.com/abc/xyz
如果控制器上没有 [Route],只有方法上有。
public class PigController:ControllerBase
{
[Route("haha/hehe")]
public IActionResult Greeting()
{
return Content("来自猪的问候");
}
}
这时候,要想访问 Greeting 方法,其URL变为:http://www.aaa.cc/haha/hehe
【总结】其实这个基于特性的路由规则是有规律的——合并模板原则。具体说就是:
1、如果控制器上有指定,就将控制器上的路由与各个方法上的路由合并;
2、如果控制器上未指定路由,那就用方法上的路由。
说白了,就是从外向内,层层合并。
以上所说的都是大家熟悉的路由玩法,下面老周要说的这种玩法比较复杂,一般不用。
那什么情况下用?
1、你觉得个个控制器去加 [Route]、[HttpPost] 等太麻烦,想来个痛快的;
2、你想弄个前缀,但这个前缀可能不是固定的。比如,加个命名空间做前缀,像 http://www.yyy.cn/MyNamespace/MyController/MyAction/Other。这个命名空间的名称要通过编程,在程序运行的时候获取,而不是硬编码。
这样的话,就可以用到应用程序模型——其实我们这一系列文章都离不开应用程序模型,因为整个MVC应用程序的自定义方式都与其有关。
所以这种方案也是通过实现自定义的约定接口来完成的,其中主要是用到 AttributeRouteModel 类。它的功能与直接用在控制器或方法上的 [Route] 特性差不多,只不过这个类能让我们通过编程的方式设置路由URL。也就是 Template 属性,它是一个字符串,跟 [Route] 中设置的URL一样的用途,比如
[Route("blogs/[controller]/[action]")]
public class KillerController : Controller ...
就相当于 AttributeRouteModel.Template = "blogs/[controller]/[action]"。在特性化的路由规则上,controller、action 这些字段都写在中括号里面。
下面老周就给大伙演示一下,主要实现:
1、以当前程序集的名称为URL前缀;
2、前缀后接控制器名称;
3、控制器名后面接操作方法名称。
假设当前程序集名为 MyHub,控制器名为 Home,操作方法为 Goodbye,那么,调用 Goodbye 方法的URL是:https://mycool.net/myhub/home/goodbye。
这个都是应用程序在运行后自动设置的,要是程序集改名为 MyGooood,那么URL前缀就自动变为 /mygooood。
从以上分析看,此约定要改控制器的路由,也要改操作方法的路由,所以,实现的约定接口应为 IControllerModelConvention。下面是代码:
public class CustControllerConvension : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
// 如果已存在可用的 Attribute Route,就跳过
if (controller.Selectors.Any(s => s.AttributeRouteModel != null))
{
return;
}
// 程序集名称
string assName = controller.ControllerType.Assembly.GetName().Name ?? "";
// 除掉名称中的“.”
assName = assName.Replace(".", "/");
// 控制器名称
string ctrlName = controller.ControllerName; // 至少要有一个Selector
if (controller.Selectors.Count == 0)
{
controller.Selectors.Add(new());
}
// 先设置Controller上的路由
foreach (var selector in controller.Selectors)
{
// Assembly name + controller name
selector.AttributeRouteModel = new()
{
Template = AttributeRouteModel.CombineTemplates(assName, ctrlName)
};
}
// 再设置Action上的路由
foreach (var action in controller.Actions)
{
if (action.Selectors.Any(s => s.AttributeRouteModel != null))
{
// 如果已有Attribute route,就跳过
continue;
}
// 至少得有一个Selector
if (action.Selectors.Count == 0)
{
action.Selectors.Add(new SelectorModel());
}
foreach (var selector in action.Selectors)
{
// Action的名字作为URL的一部分
selector.AttributeRouteModel = new()
{
Template = action.ActionName
};
}
}
}
}
不管是控制器的还是操作方法的,都允许设置多个SelectorModel对象。这就类似我们在控制器上可以设置多个 [Route]。代码在处理之前都先判断一下是不是有任何 Selector 的 AttributeRouteModel 属性不为 null,这是为了让自定义的约定与 [Route]、[HttpGet] 等特性类不冲突。我的意思是如果你在控制器或操作方法上用了 [Route] 特性,那么这里就跳过,不要再修改它。
if (controller.Selectors.Any(s => s.AttributeRouteModel != null))
{
return;
} if (action.Selectors.Any(s => s.AttributeRouteModel != null))
{
continue;
}
CombineTemplates 是静态方法,它可以帮我们自动拼接URL,只要你把两段URL传递给它就行了。
所以,上述约定类的规则就是:Assembly Name + Controller Name + Action Name。
约定完了后,还要在初始化MVC功能(注册服务)时设置一下。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers().AddMvcOptions(opt=>
{
opt.Conventions.Add(new CustControllerConvension());
});
var app = builder.Build();
注意啊,这样设置后,约定是作用于全局的,应用程序内的控制器都会应用。你如果只想局部用,那就定义了特性类(从Attribute类派生),实现原理一样的。你可以参考老周在上上篇中举到的自定义控制器名称的例子。
应用程序在映射终结点时就不用设置路由了。
app.MapControllers();
app.Run();
现在,我们定义些控制器类测试一下。
public class 大螃蟹Controller : ControllerBase
{
public IActionResult Greeting() => Content("来自螃蟹精的问候");
}
这里假设程序集的名称是 FlyApp。你应该知道怎么访问了。看图。

不过瘾的话,可以再写一个控制器类。
public class HomeController : Controller
{
public IActionResult Index()
{
return Content("来自高达的问候");
} public IActionResult Hello()
{
return Content("来自西海龙王的问候");
}
}
继续测试,看图。

可能有大伙伴会说:老周,你这样弄有意思吗?
老周答曰:没意思,图增意趣耳!
老周再曰:其实啊,这个也不是完全没用的。老周前文说过的,如果你的URL中有某部分是要通过代码来获取,而不是硬编码的话,那这种折腾就有用了。总之,一句话:技巧老周都告诉你了,至于怎么去运用,看实际需要呗。
【ASP.NET Core】MVC控制器的各种自定义:特性化的路由规则的更多相关文章
- ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门
一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...
- ASP.NET Core 入门笔记5,ASP.NET Core MVC控制器入门
摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-controller-action.html 一.前言 1.本教程主要内容 A ...
- ASP.NET Core MVC 控制器创建与依赖注入
本文翻译自<Controller activation and dependency injection in ASP.NET Core MVC>,由于水平有限,故无法保证翻译完全准确,欢 ...
- 扒一扒asp.net core mvc控制器的寻找流程
不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...
- asp.net core MVC 控制器,接收参数,数据绑定
1.参数 HttpRequest HttpRequest 是用户请求对象 QueryString Form Cookie Session Header 实例: public IActionResult ...
- Asp.Net Core MVC控制器和视图之间传值
一.Core MVC中控制器和视图之间传值方式和Asp.Net中非常类似 1.弱类型数据:ViewData,ViewBag 2.强类型数据:@model 二.代码 实例 1.ViewData pub ...
- ASP.NET Core MVC/WebAPi如何构建路由?
前言 本节我们来讲讲ASP.NET Core中的路由,在讲路由之前我们首先回顾下之前所讲在ASP.NET Core中的模型绑定这其中有一个问题是我在项目当中遇见的,我们下面首先来看看这个问题. 回顾A ...
- Pro ASP.NET Core MVC 第6版 第一章
目录 第一章 ASP.NET Core MVC 的前世今生 ASP.NET Core MVC 是一个微软公司开发的Web应用程序开发框架,它结合了MVC架构的高效性和简洁性,敏捷开发的思想和技术和.N ...
- ASP.NET Core MVC 之视图(Views)
ASP.NET Core MVC 控制器可以使用视图返回格式化的结果. 1.什么是视图 在 MVC 中,视图封装了用户与应用交互呈现细节.视图是具有生成要发送到客户端内容的,包含嵌入代码的HTML模板 ...
- ASP.NET Core MVC中的 [Required]与[BindRequired]
在开发ASP.NET Core MVC应用程序时,需要对控制器中的模型校验数据有效性,元数据注释(Data Annotations)是一个完美的解决方案. 元数据注释最典型例子是确保API的调用者提供 ...
随机推荐
- 采用阿里云 yum的方式安装ceph
首先机器需要联网,并且配置网络yum源,epel源,可从阿里开源镜像站中下载源文件. 注:EPEL (Extra Packages for Enterprise Linux)是基于Fedora的一个项 ...
- 1-VSCode搭建GD32开发环境
一.使用VSCode开发GD32的原因 1-单片机开发用的最多的IDE为Keil,而Keil为商用软件,并非开源,而且只支持windows环境,介于当前关系,有断供的风险在. 2-其他IDE类似第1条 ...
- MySQL学习(2)---MySQL数据类型
ps:此随笔基于mysql 5.7.*版本. 补充: UNSIGNED:所有整数类型都可以有一个可选(非标准)UNSIGNED属性.无符号类型可用于在列中仅允许非负数存在,或者当开发者需要该列的较大数 ...
- 源码学习之MyBatis的底层查询原理
导读 本文通过MyBatis一个低版本的bug(3.4.5之前的版本)入手,分析MyBatis的一次完整的查询流程,从配置文件的解析到一个查询的完整执行过程详细解读MyBatis的一次查询流程,通过本 ...
- 【Java8新特性】- Lambda表达式
Java8新特性 - Lambda表达式 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! ...
- 关于 LOCATE vs LIKE vs INSTR 性能分析
网上很多流传关于Mysql字符串对比的函数性能说法是 INSTR >> LOCATE >> LIKE 字符串,所以今天我自己测一下看看真假. 这是在字符串较长的情况下测试的结 ...
- `<jsp:getProperty>`动作和`<jsp:setProperty>`动作的使用在一个静态页面填写图书的基本信息,页面信息提交给其他页面,并且在其页面显示。要去将表单元素的值赋值给Java
<jsp:getProperty>动作和<jsp:setProperty>动作的使用 1.<jsp:getProperty>动作 语法格式: <jsp:get ...
- Vue3 SFC 和 TSX 方式自定义组件实现 v-model
1 v-model 1.1 理解 v-model v-model 是 vue3 中的一个内置指令,很多表单元素都可以使用这个属性,如 input.checkbox 等,咱可以在自定义组件中实现 v-m ...
- 齐博x1标签实例:标签如何调用论坛内容
论坛的内容不像CMS其它模块可以直接用变量 {$rs.content} 因为论坛的内容数据表是放在另一个表的,单独分开的. 当前也是为了考试效率问题而这样设计的. 所以他的调用要用下面的代码 {:fu ...
- Python学习三天计划-3
面向对象 一.类的定义 1.类定义 class是关键字,表示要定义类了 类的属性,即定义在类中的变量(成员变量) 类的行为,即定义在类中的函数(成员方法) 2.对象 创建类对象的语法: class S ...