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的路由值(环境值):
- 环境值: { controller = “UrlGeneration”, action = “Source” }
- 环境值传给 Url.Action: { controller = “UrlGeneration”, action = “Destination” }
- 路由模板: {controller}/{action}/{id?}
- 结果: /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的路由模板。
相关文章:
ASP.NET Core 的 Routing
ASP.NET Core 的 Routing ASP.NET Core 的 controllers 使用Routing 中间件匹配客户端的 url 请求,然后映射到对应的 controller 的处理方法(Action)上。 Actions 可以是 常规路由 或 属性路由 的映射。 MVC App一般使用常规路由。 REST API…...

IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理
IBM Spectrum LSF Explorer 适用于 IBM Spectrum LSF 集群的强大、轻量级报告解决方案 亮点 ● 允许不同的业务和技术用户使用单一解决方案快速创建和查看报表和仪表板 ● 利用可扩展的库提供预构建的报告 ● 自定义并生成性能、工作负载和资源使用情况的报…...

RHCE——十一、NFS服务器
NFS服务器 一、简介1、NFS背景介绍2、生产应用场景 二、NFS工作原理1、示例图2、流程 三、NFS的使用1、安装2、配置文件3、主配置文件分析3.1 实验1 4、NFS账户映射4.1 实验24.2 实验3 四、autofs自动挂载服务1、产生原因2、安装3、配置文件分析4、实验45、实验5 一、简介 1、…...
Python编程练习与解答 练习100:随机密码
编写一个生成最忌密码的函数,密码的长度应该在7-10个字符之间。每个字符应该从ASCII表的第33位到126位中随机选择。函数不接受任何参数,返回随机生成的密码作为位移结果。在文件的main程序中显示随机生成的密码。main程序只在解答没有被导入另一个文件时…...

华为云云服务器评测 | 从零开始:云耀云服务器L实例的全面使用解析指南
文章目录 一、前言二、云耀云服务器L实例要点介绍2.1 什么是云耀云服务器L实例2.1.1 浅析云耀云服务器L实例 2.2 云耀云服务器L实例的产品定位2.3 云耀云服务器L实例优势2.4 云耀云服务器L实例支持的镜像与应用场景2.5 云耀云服务器L实例与弹性云服务器(ECS…...

欧科云链研究院探析Facebook稳定币发行经历会不会在PayPal重演
引言 作者最近的报告-探析PayPal发行稳定币是否会重蹈Facebook覆辙-近期被英国的金融时报(中文版)刊登。由于该报告在欧科云链研究院内部反响较好,下面就带大家简单的剖析这篇报告的主要内容。 *这篇文章主要由对比分析(已删减&a…...
docker 容器pip、git安装异常;容器内web对外端口ping不通
1、docker 容器pip、git安装异常 错误信息: git clone https://github.com/vllm-project/vllm.git Cloning into ‘vllm’… fatal: unable to access ‘https://github.com/vllm-project/vllm.git/’: Failed to connect to 127.0.0.1 port 10808: Connection ref…...

SpringBoot Mybatis 多数据源 MySQL+Oracle+Redis
一、背景 在SpringBoot Mybatis 项目中,需要连接 多个数据源,连接多个数据库,需要连接一个MySQL数据库和一个Oracle数据库和一个Redis 二、依赖 pom.xml <dependencies><dependency><groupId>org.springframework.boot&l…...
【JavaScript 16】对象继承 原型对象属性 原型链 构造函数属性 instanceof运算符 继承 多重继承 模块
对象继承 原型对象概述instanceof运算符构造函数的继承多重继承模块 A 对象通过继承 B 对象,就能 直接拥有 B 对象的所有属性和方法(利于代码复用) 大部分面向对象的编程语言都是通过类(class)实现对象的继承 但 传统…...

地下管线三维自动建模软件MagicPipe3D V3.0发布
2023年9月1日经纬管网建模系统MagicPipe3D V3.0正式发布,该版本经过众多用户应用和反馈,在三维地下管线建模效果、效率、适配性等方面均有显著提升!MagicPipe3D本地离线参数化构建地下管网模型(包括管道、接头、附属设施等&#x…...

百度等8家企业首批上线大模型服务;大语言模型微调之道
🦉 AI新闻 🚀 百度等8家企业首批上线大模型服务 摘要:百度、字节、中科院旗下8家企业/机构的大模型通过备案,正式面向公众提供服务。百度旗下AI大模型产品文心一言率先开放,用户可下载App或登录官网体验。百川智能也…...

二、Mycat2 相关概念及读写分离
第三章 Mycat2 相关概念 3.1 概念描述 1、分库分表 按照一定规则把数据库中的表拆分为多个带有数据库实例,物理库,物理表访问路 径的分表。 解读:分库:一个电商项目,分为用户库、订单库等等。 分表:一张订单表数据数百万ÿ…...

react利用wangEditor写评论和@功能
先引入wangeditor写评论功能 import React, { useEffect, useState, useRef, forwardRef, useImperativeHandle } from react; import wangeditor/editor/dist/css/style.css; import { Editor, Toolbar } from wangeditor/editor-for-react; import { Button, Card, Col, For…...

Android之布局转圆角
Android之布局转圆角 文章目录 Android之布局转圆角说明一、效果图二、实现步骤1.自定义RoundRelativeLayout2.使用 总结 说明 很多需求比较无语,需要某个布局转圆角,像个显眼包一样,所以为了满足显眼包,必须整呐提示:…...

Linux的目录结构特点
Linux的目录结构特点 1、使用树形目录结构来组织和管理文件。 2、整个系统只有一个根目录(树根),Linux的根目录用“/”表示。 3、其他所有分区以及外部设备(如硬盘,光驱等)都是以根目录为起点࿰…...

【算法与数据结构】654、LeetCode最大二叉树
文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:【算法与数据结构】106、LeetCode从中序与后序遍历序列构造二叉树这两道题有些类似,相关代…...

您必须尝试的 4 种经典特征提取技术!
一、说明 特征提取如何实现?其手段并不是很多,有四个基本方法,作为AI工程师不能不知。因此,本篇将对四种特征提取给出系统的方法。 二、概述 图像分类长期以来一直是计算机视觉领域的热门话题,并希望能够保持这种状态。…...

Unity中Shader的遮罩的实现
文章目录 前言一、遮罩效果的实现主要是使用对应的纹理实现的,在属性中暴露对应的遮罩纹理,对其进行采样后,最后相乘输出即可二、如果需要像和主要纹理一样流动,则需要使用和_Time篇一样的方法实现流动即可 前言 Unity中Shader的…...
架构师成长之路|Redis key过期清除策略
Eviction policies maxmemory 100mb 当我们设置的内存达到指定的内存量时,清除策略的配置方式决定了默认行为。Redis可以为可能导致使用更多内存的命令返回错误,也可以在每次添加新数据时清除一些旧数据以返回到指定的限制。 当达到最大内存限制时,Redis所遵循的确切行为是…...
ubuntu20.04使用privoxy进行http代理转http代理,并定制http代理头(hide-user-agent的使用方法)
#sudo apt-get update;sudo apt install -y privoxy #sudo apt remove privoxyprivoxy --version; rootfv-az1239-825:/tmp# privoxy --version Privoxy version 3.0.28 (https://www.privoxy.org/) rootfv-az1239-825:/tmp# 安装完毕后,先停止服务,修改配置文件,再启动服…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学
一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件,其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时,价带电子受激发跃迁至导带,形成电子-空穴对,导致材料电导率显著提升。…...