跟我一起学.NetCore之路由的最佳实现
前言
路由,这词绝对不陌生,不管在前端还是后端都经常提到,而这节不说其他,就聊.NetCore的路由;在之前的Asp.Net MVC 中,路由算是面试时必问的考点,可见其重要性,它的主要作用是映射URL,而不需要关注服务器的物理文件结构,提高安全性,同时规范了URL请求,有利于搜索引擎优化;所以在Asp.NetCore中当然也不能缺少,以下说说其应用。
正文
在Asp.NetCore中,注册路由方式有两种:
模板路由注册:适合应用于MVC页面项目,相对于来说,使用模板的形式更加方便,约定大于配置,统一URL;
特性路由注册(RouteAttribute):适合应用于API项目,针对于不同业务路由会进行定制,特性标注显得更加便捷;
少说话,多撸码,这里就创建一个WebAPI项目来进行演示:

默认情况下,创建的WebAPI项目是推荐使用特性路由方式进行注册(MVC默认是使用路由模板方式),当然也可以在Api项目中进行使用模板路由方式注册,如下:

当注释掉特性路由,在注册终结点时增加路由模板,最终运行时会出现异常,因为用ApiController标识的Controller必须是特性路由,那把ApiController特性也注释掉,结果就正常运行了,如下:

这里其实有一个重要的知识点:路由匹配规则,考虑到小伙伴们之前使用Asp.Net MVC时就用到,先默认小伙伴们了解这块知识(抽时间单独整理一篇出来);以下内容着重说说特性路由应用和路由约束这块,因为有关注到接触的项目中不管是之前的Asp.Net, 还是现在的Asp.NetCore项目,几乎没有看到路由约束的应用,而在很多场景,路由约束很有必要,用于消除路由的歧义。
特性路由应用
直接在Action上加Route 特性,如下:

运行结果如下:

通常为了避免在每个Action中的重复的指定路径前缀,可以将公共部分提取到Controller类上进行Route标识,如下:

统一前缀之后,运行结果:

以上的方式都是将路径写成固定的字符串,有时候会要求像模板路由中那样动态替换指定标记([area],[controller],[action] ),通常项目中会如下使用:

以上只是简单说说特性路由的用法,小伙伴们可以根据实际情况进行标注,当然如果有特殊需求,也可以进行自定义路由特性,只要继承IRouteTemplateProvider接口即可,上面标注的Route和HttpGet特性也是继承于这个接口,把RouteAtrribute的源代码扒出来参考参考:
namespace Microsoft.AspNetCore.Mvc
{
// 标识此特性只能在类和方法上使用
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class RouteAttribute : Attribute, IRouteTemplateProvider
{
private int? _order;
// 构造函数,传入一个Url模板参数
public RouteAttribute(string template)
{
Template = template ?? throw new ArgumentNullException(nameof(template));
}
public string Template { get; }
// 路由执行顺序,默认为0
public int Order
{
get { return _order ?? 0; }
set { _order = value; }
}
/// <inheritdoc />
int? IRouteTemplateProvider.Order => _order;
// 路由名称,后续可以根据路由名称生成Url,而不是硬编码编写
public string Name { get; set; }
}
}
来,自己也搞一个特性路由练练:

有没有很简单,其实这里是忍住没去扒代码的,后续单独去扒;用法就先暂时说这么多,接下来说说路由传参和约束。
路由传参和约束
一般情况,会针对路由中动态参数进行约束;从而路由约束可能会被作为输入校验,但是官方不推荐,因为当规则不匹配的时候,返回状态404(找不到),而事实对应地址可以到达请求,只是参数输入不合法而已,所以官方推荐作为消除路由歧义 ,同时也能避免不规则的路由进行业务处理;直接来案例演示(废话多了怕掉坑里):

新增的Controller完整代码:
namespace RouteDemo.Controllers
{
[ApiController]
[Route("/api/[controller]")]
public class TestConstraintController
{
/// <summary>
/// 验证必须输入,没有输入就返回404
/// </summary>
[HttpGet("TestRequiredConstraint/{name:required}")]
public string TestRequiredConstraint(string name)
{
return "TestRequiredConstraint";
}
/// <summary>
/// 整型约束,非整型数据就返回404
/// </summary>
[HttpGet("TestValueConstraint/{id:int}")]
public string TestValueConstraint(int id)
{
return "TestValueConstraint";
}
/// <summary>
/// 传入最小值为100,传入值小于100就返回404
/// </summary>
[HttpGet("TestMinConstraint/{id:min(100)}")]
public string TestMinConstraint(int id)
{
return "TestMinConstraint";
}
/// <summary>
/// 范围约束8到18,不在这个范围内就返回404
/// </summary>
[HttpGet("TestRangeConstraint/{id:range(8,18)}")]
public string TestRangeConstraint(int id)
{
return "TestRangConstraint";
}
/// <summary>
/// 验证最小长度为5,低于这个长度就返回404
/// </summary>
[HttpGet("TestMinLengthConstraint/{name:minLength(5)}")]
public string TestMinLengthConstraint(string name)
{
return "TestMinLengthConstraint";
}
/// <summary>
/// 正则表达式约束,以三个数字开头,不满足就返回404
/// </summary>
[HttpGet("TestRegexConstraint/{msg:regex(^\\d{{3}})}")]
public string TestRegexConstraint(string msg)
{
return "TestRegexConstraint";
}
/// <summary>
/// 整型约束,范围约束8到18,不在这个范围内就返回404
/// </summary>
[HttpGet("TestMultiConstraint/{id:int:range(8,18)}")]
public string TestMultiConstraint(int id)
{
return "TestRangConstraint";
}
}
}
以上只是挑了几个默认内置的约束进行举例演示,其实还有很多,剩下的小伙伴下来一定要试试,用法都很简单,如下:
单个约束时
参数:约束
案例:[HttpGet("TestRequiredConstraint/{name:required}")]
多个约束时
参数:约束1:约束2:....
案例:[HttpGet("TestMultiConstraint/{id:int:range(8,18)}")]
框架本身内置的约束,如下(小伙伴一定要敲敲):
| 约束 | 示例 | 说明 |
|---|---|---|
int |
{id:int} |
匹配任何整数 |
bool |
{active:bool} |
匹配 true 或 false。 不区分大小写 |
datetime |
{dob:datetime} |
在固定区域性中匹配有效的 DateTime 值。 |
decimal |
{price:decimal} |
在固定区域性中匹配有效的 decimal 值。 |
double |
{weight:double} |
在固定区域性中匹配有效的 double 值。 |
float |
{weight:float} |
在固定区域性中匹配有效的 float 值。 |
guid |
{id:guid} |
匹配有效的 Guid 值 |
long |
{ticks:long} |
匹配有效的 long 值 |
minlength(value) |
{username:minlength(4)} |
字符串必须至少为 4 个字符 |
maxlength(value) |
{filename:maxlength(8)} |
字符串不得超过 8 个字符 |
length(length) |
{filename:length(12)} |
字符串必须正好为 12 个字符 |
length(min,max) |
{filename:length(8,16)} |
字符串必须至少为 8 个字符,且不得超过 16 个字符 |
min(value) |
{age:min(18)} |
整数值必须至少为 18 |
max(value) |
{age:max(120)} |
整数值不得超过 120 |
range(min,max) |
{age:range(18,120)} |
整数值必须至少为 18,且不得超过 120 |
alpha |
{name:alpha} |
字符串必须由一个或多个字母字符组成,a-z,并区分大小写。 |
regex(expression) |
{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)} |
字符串必须与正则表达式匹配。 请参阅有关定义正则表达式的提示。 |
required |
{name:required} |
用于强制在 URL 生成过程中存在非参数值 |
不用说,内置约束肯定不可能面面俱到,所以针对个性化约束,还需要自定义,继承 IRouteConstraint 接口便可实现自定义路由约束。 接口中包含 Match,当满足约束时,它返回 true,否则返回 false。如下例演示:


总结
特性路由和约束就说到这吧,根据实际需求进行路由约束,但不能盲目,如果是要进行输入数据校验,请考虑使用模型验证!下一节说说集成Swagger。
------------------------------------------------
CSDN:Code综艺圈
知乎:Code综艺圈
掘金:Code综艺圈
博客园:Code综艺圈
bilibili:Code综艺圈
------------------------------------------------
一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

撸文不易,莫要白瞟,三连走起~~~~
跟我一起学.NetCore之路由的最佳实现的更多相关文章
- 跟我一起学.NetCore之静态文件处理的那些事
前言 如今前后端分离开发模式如火如荼,开发职责更加分明(当然前后端一起搞的模式也没有完全褪去):而对于每个公司产品实施来说,部署模式会稍有差别,有的会单独将前端文件部署为一个站点,有的会将前端文件和后 ...
- 教你如何在Drcom下使用路由器上校园网(以广东工业大学、极路由1S HC5661A为例)
免责声明: 在根据本教程进行实际操作时,如因您操作失误导致出现的一切意外,包括但不限于路由器变砖.故障.数据丢失等情况,概不负责: 该技术仅供学习交流,请勿将此技术应用于任何商业行为,所产生的法律责任 ...
- 跟我一起学.NetCore之选项(Options)核心类型简介
前言 .NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型:从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加 ...
- 跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt)
前言 撸码需谨慎,裸奔有风险.经常在一些技术交流群中了解到,还有很多小伙伴的项目中Api接口没有做任何安全机制验证,直接就裸奔了,对于一些临时项目或是个人小项目还好,其余的话,建议小伙伴们酌情考虑都加 ...
- 跟我一起学.NetCore之熟悉的接口权限验证不能少(Jwt)
前言 权限管控对于一个系统来说是非常重要的,最熟悉不过的是菜单权限和数据权限,上一节通过Jwt实现了认证,接下来用它实现接口权限的验证,为什么不是菜单权限呢?对于前后端分离而言,称其为接口权限感觉比较 ...
- 跟我一起学.NetCore之MVC过滤器,这篇看完走路可以仰着头走
前言 MVC过滤器在之前Asp.Net的时候就已经广泛使用啦,不管是面试还是工作,总有一个考点或是需求涉及到,可以毫不疑问的说,这个技术点是非常重要的: 在之前参与的面试中,得知很多小伙伴只知道有一两 ...
- 从0系统学Android--2.6 Activity 的最佳实践
从0系统学Android--2.6 Activity 的最佳实践 本系列文章目录:更多精品文章分类 本系列持续更新中.... 实践中的技巧 2.6.1 知晓当前是在哪个 Activity 这个其实很简 ...
- [ruby on rails] 跟我学之(4)路由映射
前面<[ruby on rails] 跟我学之Hello World>提到,路由对应的文件是 config/routes.rb 实际上我们只是添加了一句代码: resources :pos ...
- 跟我一起学.NetCore之日志(Log)模型核心
前言 鲁迅都说:没有日志的系统不能上线(鲁迅说:这句我没说过,但是在理)!日志对于一个系统而言,特别重要,不管是用于事务审计,还是用于系统排错,还是用于安全追踪.....都扮演了很重要的角色:之前有很 ...
随机推荐
- vue-cli的安装及版本查看/更新
vue-cli的安装及版本查看更新 vue-cli安装 npm install vue-cli -g vue-cli的版本查看 vue -V vue-cli的3.0+以后使用的不是vue-cli了,如 ...
- MongoDB最新4.2.7版本三分片集群修改IP实操演练
背景 重新组网,需要对现有MongoDB分片集群服务器的IP进行更改,因此也需要对MongoDB分片集群的IP也进行相应的更新,而MongoDB分片集群的IP修改不能单纯的通过配置来进行,需要一番折腾 ...
- Java树形结构中根据父类节点查找全部子类节点
上一篇文章介绍了两种树形结构数据整合json格式的方法,第一种方法中有根据父类获取全部子类的方法,这里单独拿出来再说一下. 仍然是利用递归来整合,代码如下: //根据父节点获取全部子节点 public ...
- 2020.5.28 第八篇 Scrum冲刺博客
Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.每日站立会议 1.1 会议照片 1.2 项目完成情况 二.项目燃尽图 三.签入记录 3.1 代码/文档签入记录 3. ...
- python数据处理工具 -- pandas(序列与数据框的构造)
Pandas模块的核心操作对象就是对序列(Series)和数据框(Dataframe).序列可以理解为数据集中的一个字段,数据框是值包含至少两个字段(或序列) 的数据集. 构造序列 1.通过同质的列表 ...
- Java多线程_并发容器ConcurrentHashMap/CopyOnWriteArrayList/CopyOnWriteArraySet
ConcurrentHashMap HashMap是线程不安全的,可以使用Collections.synchronizedMap(map)把一个不安全的map变成安全的,但是这里可以直 ...
- 启用valgrind的MPI支持
TL;DR sudo apt install valgrind-mpi 内存泄漏和越界问题,是C/C++程序常见问题.有一些工具提供了检测内存泄漏的功能,如 valgrind 的 memchecker ...
- javaScript高级含Es6
JavaScript高级第01天笔记 1.面向过程与面向对象 1.1面向过程 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了. 1.2 ...
- os.path获取当前路径及父路径
import os pwd = os.getcwd() print("当前目录: " + pwd) father_path_method1 = os.path.dirname(pw ...
- 03.AOF持久化机制配置与工作流程
一.AOF持久化的配置 配置文件redis.conf,AOF持久化默认是关闭的,默认是打开RDB持久化 appendonly yes 二.工作流程: 打开AOF持久化机制之后,redis每次接 ...