随着技术的发展,ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生,或其他想从事ASP.NET Core MVC 系统开发的人员。 经过前几篇文章的讲解,初步了解ASP.NET Core MVC项目创建,启动运行,以及ASP.NET Core MVC的命名约定,创建控制器,视图,模型,接收参数,传递数据等内容,今天继续讲解ASP.NET Core MVC 路由等相关内容,仅供学习分享使用。
什么是路由?
路由是一种机制,主要是用于检查每一个用户请求,将用户请求映射到Action中,这一动作通过路由中间件来实现。ASP.NET Core MVC使用路由中间件来匹配传入请求的URL并将它们映射到操作(Action方法)。
默认路由
在通过模板创建ASP.NET Core MVC中,默认会添加路由中间件,并提供一种默认的路由映射规则和约束。
MapControllerRoute 用于创建单个路由。 单个路由命名为 default
路由。 大多数具有控制器和视图的应用都使用类似 default
路由的路由模板。如下所示:
using Microsoft.AspNetCore.Server.Kestrel.Core; using System.Text.Encodings.Web; using System.Text.Unicode; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews().AddJsonOptions(options => { options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); }); builder.Services.Configure<KestrelServerOptions>(options => { options.AllowSynchronousIO = true; }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); //1. 添加路由中间件EndpointRoutingMiddleware app.UseRouting(); app.UseAuthorization(); //2.为控制器和Action添加一种路由映射规则,包括名称,规则,约束等 app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
在上述代码中,创建默认路由的关键代码如下所示:
app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
其中各个属性说明:
- name表示路由名称,默认值为default。
- pattern 为匹配模板,默认值为"{controller=Home}/{action=Index}/{id?}"。其中默认controller=Home,表示缺省值为HomeController,action=Index,表示默认缺省值为Index。id后面的?表示为可空类型,可以根据需要来填写。
默认路由示例
- 如:/ 表示controller和action都采用默认值,相当于/Home/Index。
- 如:/Hello表示只指定了controller,action为默认缺省值,相当于/Hello/Index。
- 如:/Hello/Test/5 根据路由匹配规则,提取出controller=HelloController , action=Test , id =5 。如果刚好存在此controller和action,则匹配导航成功。
创建默认路由的简便写法,可通过以下代码替代上面的app.MapControllerRoute,如下所示:
//创建默认传统路由的简便写法 app.MapDefaultControllerRoute();
以上是传统路由的示例。 之所以称为传统路由,是因为它为 URL 路径建立了一个约定:
- 第一个路径段
{controller=Home}
映射到控制器名称。 - 第二段
{action=Index}
映射到操作名称。 - 第三段
{id?}
用于可选id
。{id?}
中的?
使其成为可选。id
用于映射到模型实体。
此传统路由映射具有以下特点:
- 仅基于控制器和操作名称。
- 不基于命名空间、源文件位置或方法参数。
使用默认路由进行传统路由可以创建应用,而无需为每个操作提出新的 URL 模式,有助于简化代码。使 UI 更具可预测性。
多个路由
在实际应用中,可以设置多个路由,为某些特定的需求设置专有路由。设置多个路由,如下所示:
//2.为控制器和Action添加一种路由映射规则,包括名称,规则,约束等 app.MapControllerRoute( name: "blog", pattern: "blog/{*article}", defaults: new { controller = "Blog", action = "Article" }); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
在上述代码中,名称为blog的路由,虽然采用的是传统路由,但只用于特定操作。
在创建多个路由时,以下几点需要注意:
blog
路由比default
路由具有更高的匹配优先级,因为它最先添加。- 路由名称为路由指定逻辑名称,使用命名路由可以简化 URL 创建,在应用程序范围内必须唯一。
- 默认情况下,传统路由依赖于顺序。 一般情况下,具有区域的路由应放在路由表中靠前的位置,因为它们比没有区域的路由更具体。也就是:特殊的在前面,通用的在后面。
不明确操作
如果同一个请求有两个action都满足路由终结点的匹配,那么路由会进行如下处理:
- 选择最佳的候选项。
- 引发异常。
如下代码,在请求时,会两个都满足路由匹配规则:
/// <summary> /// 请求需要编辑的学生信息 /// </summary> /// <param name="id"></param> /// <returns></returns> public IActionResult Edit(int id) { var student = new Student() { Id = 1, Name = "公子小六", Age = 21, Sex = "男" }; return View(); } /// <summary> /// 编辑后保存学生信息 /// </summary> /// <param name="id"></param> /// <param name="student"></param> /// <returns></returns> public IActionResult Edit(int id, Student student) { //保存学生 return View(); }
那么路由中间件就会跑出异常,如下所示:
在这种情况下,要解析正确的路由,需要通过Http谓词来区分,只有当请求为Post时,才会请求Edit(int id,Student student);其他请求时【一般为Get】,匹配Edit(int id)。添加Http谓词后的代码如下:
/// <summary> /// 请求需要编辑的学生信息 /// </summary> /// <param name="id"></param> /// <returns></returns> public IActionResult Edit(int id) { var student = new Student() { Id = 1, Name = "公子小六", Age = 21, Sex = "男" }; return View(); } /// <summary> /// 编辑后保存学生信息 /// </summary> /// <param name="id"></param> /// <param name="student"></param> /// <returns></returns> [HttpPost] public IActionResult Edit(int id, Student student) { //保存学生 return View(); }
属性路由
属性路由一般用于WebAPI,使用一组属性将操作直接映射到路由模板,将应用功能建模为一组资源。属性路由使用[Route(template)]标记于controller或action中,示例如下所示:
public class TestController : Controller { [Route("Test1")] [Route("Test1/Index")] [Route("Test1/Index/{id?}")] public IActionResult Index(int id) { ViewBag.Id = id; return View(); } public IActionResult Test() { return View(); } }
运行程序,在浏览器中,输入网址【https://localhost:7116/Test1/Index/10】,如下所示:
注意:属性路由中,也可用标记:[Route("[controller]/[action]")],效果可传统路由一致。
属性路由与传统路由对比
经过以上示例,属性路由与传统路由,主要由以下几点差异:
- 属性路由需要更多输入才能指定路由,属性路由自定义比较强,更能精准控制路由。
- 传统默认路由会更简洁地处理路由。
- 属性路由优先级高于传统路由。对于属性路由,控制器和操作名称在操作匹配中不起作用,除非使用标记替换。
- 属性路由支持定义多个访问同一操作的路由,意味着每个路由属性都与操作方法上的每个路由属性相结合。
- 属性路由支持使用与传统路由相同的内联语法,来指定可选参数、默认值和约束。如:[HttpPost("product14/{id:int}")]
保留关键字
在ASP.NET Core MVC项目中,会有一些关键字,作为路由参数名称,如下所示:
action
area
controller
handler
page
在 Razor 视图或 Razor 页面的上下文中保留以下关键字:
page
using
namespace
inject
section
inherits
model
addTagHelper
removeTagHelper
一个常见错误是使用 page
作为属性路由的路由参数。 这样做会导致与 URL 生成不一致和令人困惑的行为。错误示例如下所示:
public class TestController : Controller { [Route("/articles/{page}")] public IActionResult ListArticles(int page) { return View(page); } }
Http谓词和路由模板
在ASP.NET Core MVC项目中,具有以下几种谓词,用于区分不同的请求方式:
- [HttpGet]
- [HttpPost]
- [HttpPut]
- [HttpDelete]
- [HttpHead]
- [HttpPatch]
ASP.NET Core 具有以下路由模板:
- 所有 HTTP 谓词模板都是路由模板。
- [Route]
混合路由:属性路由与传统路由
ASP.NET Core 应用可以混合使用传统路由和属性路由。 通常,对为浏览器提供 HTML 页的控制器使用传统路由,对为 API 提供服务 REST 的控制器使用属性路由。
操作既支持传统路由,也支持属性路由。 通过在控制器或操作上放置路由可实现属性路由。 不能通过传统路由访问定义属性路由的操作,反之亦然。 控制器上的任何路由属性都会使控制器中的所有操作使用属性路由。
属性路由和传统路由使用相同的路由引擎。
以上就是ASP.NET Core MVC从入门到精通之路由的全部内容,旨在抛砖引玉,一起学习,共同进步。