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/
  • 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/MingsonZheng/ArchitectTrainingCamp/tree/main/HelloApi

课程链接

https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

.NET 云原生架构师训练营(模块二 基础巩固 路由与终结点)--学习笔记的更多相关文章

  1. .NET 云原生架构师训练营(权限系统 RGCA 开发任务)--学习笔记

    目录 目标 模块拆分 OPM 开发任务 目标 基于上一讲的模块划分做一个任务拆解,根据任务拆解实现功能 模块拆分 模块划分已经完成了边界的划分,边界内外职责清晰 OPM 根据模块拆分画出 OPM(Ob ...

  2. .NET 云原生架构师训练营(权限系统 代码实现 ActionAccess)--学习笔记

    目录 开发任务 代码实现 开发任务 DotNetNB.Security.Core:定义 core,models,Istore:实现 default memory store DotNetNB.Secu ...

  3. .NET 云原生架构师训练营(权限系统 代码实现 WebApplication)--学习笔记

    目录 开发任务 代码实现 开发任务 DotNetNB.Security.Core:定义 core,models,Istore:实现 default memory store DotNetNB.WebA ...

  4. .NET 云原生架构师训练营(权限系统 系统演示 ActionAccess)--学习笔记

    目录 模块拆分 环境配置 默认用户 ActionAccess 模块拆分 环境配置 mysql migration mysql docker pull mysql docker run -p 3306: ...

  5. .NET 云原生架构师训练营(权限系统 系统演示 EntityAccess)--学习笔记

    目录 模块拆分 EntityAccess 模块拆分 EntityAccess 实体权限 属性权限 实体权限 创建 student https://localhost:7018/Student/dotn ...

  6. .NET 云原生架构师训练营(权限系统 代码实现 EntityAccess)--学习笔记

    目录 开发任务 代码实现 开发任务 DotNetNB.Security.Core:定义 core,models,Istore:实现 default memory store DotNetNB.Secu ...

  7. .NET 云原生架构师训练营(权限系统 代码实现 Identity)--学习笔记

    目录 开发任务 代码实现 开发任务 DotNetNB.Security.Core:定义 core,models,Istore:实现 default memory store DotNetNB.Secu ...

  8. .NET 云原生架构师训练营(模块一 架构师与云原生)--学习笔记

    目录 什么是软件架构 软件架构的基本思路 单体向分布式演进.云原生.技术中台 1.1 什么是软件架构 1.1.1 什么是架构? Software architecture = {Elements, F ...

  9. .NET 云原生架构师训练营(建立系统观)--学习笔记

    目录 目标 ASP .NET Core 什么是系统 什么是系统思维 系统分解 什么是复杂系统 作业 目标 通过整体定义去认识系统 通过分解去简化对系统的认识 ASP .NET Core ASP .NE ...

  10. .NET 云原生架构师训练营(权限系统 RGCA 架构设计)--学习笔记

    目录 项目核心内容 实战目标 RGCA 四步架构法 项目核心内容 无代码埋点实现对所有 API Action 访问控制管理 对 EF Core 实体新增.删除.字段级读写控制管理 与 Identity ...

随机推荐

  1. ICDM'23 BICE论文解读:基于双向LSTM和集成学习的模型框架

    本文分享自华为云社区<ICDM'23 BICE论文解读>,作者:云数据库创新Lab. 导读 本文<Efficient Cardinality and Cost Estimation ...

  2. P1164-DP【橙】

    这道题让我更深入的理解了记忆化搜索的过程,既然记忆化搜索的结果要靠返回值来传递,那么记忆化搜索解决问题的必须是倒序的,即记忆化搜索是一个简化问题倒序解决的过程,普通搜索是一个复杂化问题逐步尝试并记录尝 ...

  3. LightOJ 1094

    题意:就是求一个树的直径,也就是求任意两点的最大距离. 做法:跑两遍DFS,详见代码. #include<iostream> #include<cstdio> #include ...

  4. yakit的web fuzzer功能的使用

    问题 yakit没有Burp 的 Intruder 爆破模块,那么yakit该怎么进行参数爆破?yakit参数爆破的方式与burp有什么区别? 前言 手工测试场景中需要渗透人员对报文进行反复的发送畸形 ...

  5. sublime_text4 2023最新版 激活教程

    官网 Sublime HQ - Remarkable Software 东西在教学的时候还是挺好用的,就是要付费购买,穷,没钱 买不起,自己动手丰衣足食. 下载安装包 我现在最新版是4.4152 下面 ...

  6. 【scikit-learn基础】--『监督学习』之 空间聚类

    空间聚类算法是数据挖掘和机器学习领域中的一种重要技术. 本篇介绍的基于密度的空间聚类算法的概念可以追溯到1990年代初期.随着数据量的增长和数据维度的增加,基于密度的算法逐渐引起了研究者的关注.其中, ...

  7. 如何与chatgpt共存

    作为程序员,专注于创造性劳动,而把重复性劳动任务交给chatgpt,要成为 需求 和 chatgpt的桥梁. 人工智能比如chatgpt越来越强,提问能力是人类的天赋,提问能力更为重要.

  8. Java中内存四区

    这里简要说明这四个区域通常用于存储的变量类型: 栈区(Stack): 存放局部变量.方法参数.返回地址等. 变量的生命周期与其所在的方法(函数)的调用周期一致. 堆区(Heap): 主要用于动态分配内 ...

  9. [转帖]tidb-系统内核调优及对比

    一.背景 验证系统调优对性能的影响,用sysbench做了一些简单的测试,具体调整方法可见官方文档 二.特殊说明 1.透明大页查看 # 查看透明大页是否开启,[]在always处表示开启,[]在nev ...

  10. [转帖]Kafka主题与分区

    https://zhuanlan.zhihu.com/p/428845986#:~:text=%E4%B8%80%E3%80%81kafka-topics.sh%E6%93%8D%E4%BD%9C%2 ...