.NET 云原生架构师训练营(模块二 基础巩固 路由与终结点)--学习笔记
2.3.3 Web API -- 路由与终结点
- 路由模板
- 约定路由
- 特性路由
- 路由冲突
- 终结点
ASP.NET Core 中的路由:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/routing?view=aspnetcore-5.0
UseRouting 添加路由中间件到管道,路由中间件用来匹配 url 和具体的 endpoint,然后执行 endpoint
UseEndpoints 添加或者注册 endpoint 到程序中,使得路由中间件可以发现它们
- MapRazorPages for Razor Pages 添加所有 Razor Pages 终结点
- MapControllers for controllers 添加所有 controller 终结点
- MapHub for SignalR 添加 SignalR 终结点
- MapGrpcService for gRPC 添加 gRPC 终结点
路由模板
路由模板由 token 和其他特定字符组成。比如“/”,特定字符进行路由匹配的时候必须全部匹配
/hello/{name:alpha}
{name:alpha} 是一段 token,一段 token 包括一个参数名,可以跟着一个约束(alpha)或者一个默认值(mingson),比如 {name=mingson} ,或者直接 {name}
app.UseEndpoints(endpoints => { //endpoints.MapControllers(); endpoints.MapGet("/hello/{name:alpha}", async context => { var name = context.Request.RouteValues["name"]; await context.Response.WriteAsync($"Hello {name}!"); }); });
路由模板中的参数被存储在 HttpRequest.RouteValues 中
大小写不敏感
url 中如果有符合,在模板中用{}代替
catch-all 路由模板
- 在 token 前用 * 或者 ** 加在参数名前,比如 blog/{*slug}
- blog/ 后面的字符串会当成 slug 的路由参数值,包括 "/",比如浏览器输入 blog/my/path 会匹配成 foo/my%2Fpath,如果想要得到 blog/my/path 则使用两个 ,foo/{path}
- 字符串.也是可选的,比如 files/{filename}.{ext?},如果要输入 /files/myFile 也能匹配到这个路由
//app.Run(async context => //{ // await context.Response.WriteAsync("my middleware 2"); //}); app.UseEndpoints(endpoints => { //endpoints.MapControllers(); // 将终结点绑定到路由上 endpoints.MapGet("/hello", async context => { await context.Response.WriteAsync("Hello World!"); }); });
启动程序,访问:https://localhost:5001/hello
输出如下:
my middleware 1Hello World!
获取路由模板参数
endpoints.MapGet("/blog/{*title}", async context => { var title = context.Request.RouteValues["title"]; await context.Response.WriteAsync($"blog title: {title}"); });
启动程序,访问:https://localhost:5001/blog/my-title
输出如下:
my middleware 1blog title: my-title
constraint 约束
[Route("users/{id:int:min(1)}")] public User GetUserById(int id) { }
app.UseEndpoints(endpoints => { endpoints.MapGet("{message:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}", context => { return context.Response.WriteAsync("inline-constraint match"); }); });
约定路由
默认
endpoints.MapDefaultControllerRoute();
自定义
endpoints.MapControllerRoute("default","{controller=Home}/{action=Index}/{id?}");
// 约定路由 app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); // 约定路由也可以同时定义多个 app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "blog", pattern: "blog/{*article}", defaults: new {controller = "blog", action = "Article"}); });
特性路由
controller
[Route("[controller]")]
http method
[HttpGet("option")]
[HttpGet]
[Route("option")]
[HttpGet]
[Route("option/{id:int}")]
路由冲突
[HttpGet] //[Route("option")] public IActionResult GetOption() { return Ok(_myOption); }
如果路由相同,启动程序会报错:
AmbiguousMatchException: The request matched multiple endpoints. Matches: HelloApi.Controllers.ConfigController.GetOption (HelloApi) HelloApi.Controllers.ConfigController.GetConfigurations (HelloApi)
终结点
ASP.NET Core 终结点是:
- 可执行:具有 RequestDelegate。
- 可扩展:具有元数据集合。
- Selectable:可选择性包含路由信息。
- 可枚举:可通过从 DI 中检索 EndpointDataSource 来列出终结点集合。
终结点可以:
- 通过匹配 URL 和 HTTP 方法来选择。
- 通过运行委托来执行。
中间件的每一步都在匹配终结点,所以路由和终结点之间的中间件可以拿到终结点的信息
app.UseRouting(); // 路由和终结点之间的中间件可以拿到终结点的信息 app.Use(next => context => { // 获取当前已经被选择的终结点 var endpoint = context.GetEndpoint(); if (endpoint is null) { return Task.CompletedTask; } // 输出终结点的名称 Console.WriteLine($"Endpoint: {endpoint.DisplayName}"); // 打印终结点匹配的路由 if (endpoint is RouteEndpoint routeEndpoint) { Console.WriteLine("Endpoint has route pattern: " + routeEndpoint.RoutePattern.RawText); } // 打印终结点的元数据 foreach (var metadata in endpoint.Metadata) { Console.WriteLine($"Endpoint has metadata: {metadata}"); } return Task.CompletedTask; }); app.UseEndpoints(endpoints => { //endpoints.MapControllers(); // 将终结点绑定到路由上 endpoints.MapGet("/blog/{title}", async context => { var title = context.Request.RouteValues["title"]; await context.Response.WriteAsync($"blog title: {title}"); }); });
启动程序,访问:https://localhost:5001/blog/my-first-blog
控制台输出如下:
Endpoint: /blog/{title} HTTP: GET Endpoint has route pattern: /blog/{title} Endpoint has metadata: System.Runtime.CompilerServices.AsyncStateMachineAttribute Endpoint has metadata: System.Diagnostics.DebuggerStepThroughAttribute Endpoint has metadata: Microsoft.AspNetCore.Routing.HttpMethodMetadata
打印 http 方法
// 打印终结点的元数据 foreach (var metadata in endpoint.Metadata) { Console.WriteLine($"Endpoint has metadata: {metadata}"); // 打印 http 方法 if (metadata is HttpMethodMetadata httpMethodMetadata) { Console.WriteLine($"Current Http Method: {httpMethodMetadata.HttpMethods.FirstOrDefault()}"); } }
启动程序,访问:https://localhost:5001/blog/my-first-blog
控制台输出如下:
Current Http Method: GET
修改终结点名称、元数据
app.UseEndpoints(endpoints => { //endpoints.MapControllers(); // 将终结点绑定到路由上 endpoints.MapGet("/blog/{title}", async context => { var title = context.Request.RouteValues["title"]; await context.Response.WriteAsync($"blog title: {title}"); }).WithDisplayName("Blog")// 修改名称 .WithMetadata("10001");// 修改元数据 });
- 调用 UseRouting 之前,终结点始终为 null。
- 如果找到匹配项,则 UseRouting 和 UseEndpoints 之间的终结点为非 null。
- 如果找到匹配项,则 UseEndpoints 中间件即为终端。 稍后会在本文档中定义终端中间件。
- 仅当找不到匹配项时才执行 UseEndpoints 后的中间件。
GitHub源码链接:
https://github.com/MINGSON666/Personal-Learning-Library/tree/main/ArchitectTrainingCamp/HelloApi
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
- .NET 云原生架构师训练营(模块二 基础巩固 MVC终结点)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 MongoDB 更新和删除)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 MySQL环境准备)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 MongoDB 聚合)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 关系)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 RabbitMQ HelloWorld)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 MongoDB API重构)--学习笔记
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 基础与配置)--学习笔记
- 黑马训练营-学习笔记----Java基础
- Laravel 学习笔记——路由(基础)
- python 0基础学习笔记6:OS模块、窗口控制、语言、内存修改
- Linux netfilter 学习笔记 之十四 netfilter模块会修改数据包关联的路由缓存吗
- python基础学习笔记<内建模块与第三方模块>
- 1机器学习和深度学习综述(百度架构师手把手带你零基础实践深度学习原版笔记系列)
- laravel 学习笔记——路由(基础)
- .Net学习笔记----2015-07-21(C#基础复习05,里氏转换)
- 原生js学习笔记——Ajax基础
- Python学习基础笔记——modules(模块操作)
- node.js 学习笔记(三)模块基础