news 2026/10/15 17:46:41

从中间件中获取当前 Endpoint:practical-aspnetcore 端点路由实战(HttpContext.GetEndpoint 详解)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从中间件中获取当前 Endpoint:practical-aspnetcore 端点路由实战(HttpContext.GetEndpoint 详解)
  • 示例工程

【免费下载链接】practical-aspnetcore

Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.

项目地址:https://gitcode.com/gh_mirrors/pr/practical-aspnetcore
点击查看免费下载

本文基于 practical-aspnetcore 仓库中的projects/endpoint-routing/new-routing-10示例,讲解如何在自定义中间件中通过HttpContext.GetEndpoint()扩展方法获取当前正在执行的 Endpoint 对象,并据此实现"按端点定制行为"的中间件逻辑。读完本文,你将掌握 Endpoint 与中间件的关系、GetEndpoint()的调用方式与空值防护、以及如何借助Endpoint.Metadata和IEndpointFeature做更深入的路由内省。

背景:Endpoint Routing 时代,中间件如何"看见"路由

从 ASP.NET Core 3.0 引入端点路由(Endpoint Routing)开始,应用的请求处理被统一抽象为"匹配 Endpoint → 执行 Endpoint"两个阶段。中间件(Middleware)位于请求管线之中,在UseRouting之后、UseEndpoints之前执行时,已经能够感知"这条请求最终会命中哪个 Endpoint"。

在旧模型下,中间件想要知道请求对应哪个路由、哪个控制器或页面,往往需要自己解析路径、手动查路由表;而在端点路由模型下,框架已经替你完成了匹配,你只需要通过HttpContext.GetEndpoint()把匹配结果"取出来"即可。

new-routing-10 示例 的核心就是这一行 API:

Use the brand newHttpContext.GetEndPointextension method to examine the current endpoint that is being executed.

(注:README 中写作GetEndPoint,实际源码中 API 名称为GetEndpoint(),使用时应以代码为准。)

示例项目全景:一个"打招呼"的中间件

示例项目位于 projects/endpoint-routing/new-routing-10,结构非常精简:

new-routing-10/ ├── Pages/ │ ├── about.cshtml # 展示中间件写入的问候语 │ └── index.cshtml # 首页,提供指向 /about 的链接 ├── Program.cs # 应用装配 + GreeterMiddleware 实现 ├── README.md └── new-routing-10.csproj

项目目标:当请求命中/about这个 Razor Page 端点时,GreeterMiddleware在转发请求之前向HttpContext.Items写入一条问候语,about.cshtml再把这条问候语渲染到页面上。判断"是否命中/about"的手段,正是从HttpContext中取出当前 Endpoint 并检查其DisplayName。

目标框架与编译配置

new-routing-10.csproj 内容如下:

<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <ImplicitUsings>true</ImplicitUsings> <LangVersion>preview</LangVersion> </PropertyGroup> </Project>

需要说明的适用前提:

  • 示例面向net10.0,并启用了LangVersion=preview,这是该仓库实验性示例的常见配置;
  • 本项目使用了隐式using(ImplicitUsings),因此Endpoint、RequestDelegate等类型无需显式引入命名空间;
  • HttpContext.GetEndpoint()扩展方法自 ASP.NET Core 3.0 起便已可用,并非 .NET 10 独占,只是该示例在 net10.0 环境下演示。

核心 API:HttpContext.GetEndpoint() 是什么

HttpContext.GetEndpoint()是定义在Microsoft.AspNetCore.Http命名空间下的扩展方法,签名形如:

public static Endpoint? GetEndpoint(this HttpContext context);

它从当前请求上下文的特性集合(Features)中读取端点路由写入的IEndpointFeature,返回与该请求匹配的Endpoint对象。返回值是可空的(Endpoint?):当请求没有匹配到任何端点(例如未经UseRouting匹配、或匹配失败进入后续未映射的处理)时,返回null。

Endpoint对象携带的关键信息包括:

成员含义本例中的值
DisplayName端点的显示名称Razor Page/about对应"/about"
Metadata端点元数据集合(EndpointMetadataCollection)可存放MessageAttribute等自定义元数据
RequestDelegate端点最终执行的委托由 Razor Pages / MVC 映射时自动设置

在端点路由模型中,Razor Page 通过@page指令声明路由模板,页面about.cshtml的@page指令没有指定模板,因此其路由默认为页面路径/about,DisplayName也即"/about"。

完整代码拆解:GreeterMiddleware 如何按端点分流

Program.cs 完整代码如下:

var builder = WebApplication.CreateBuilder(); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); var app = builder.Build(); app.UseMiddleware<GreeterMiddleware>(); app.MapRazorPages(); app.Run(); public class GreeterMiddleware { RequestDelegate _next; readonly LinkGenerator _linkGenerator; public GreeterMiddleware(RequestDelegate next, LinkGenerator linkGenerator) { _next = next; _linkGenerator = linkGenerator; } public async Task InvokeAsync(HttpContext httpContext) { Endpoint endPoint = httpContext.GetEndpoint(); if (endPoint.DisplayName == "/about") { httpContext.Items.Add("GreetingFromMiddleWare", "Hello world from GreetingMiddleware"); } await _next.Invoke(httpContext); } }

逐段说明其工作原理:

  1. 服务注册:AddControllersWithViews()与AddRazorPages()同时注册,说明本示例是"Razor Pages + MVC(带视图)"的混合装配,与仓库中 new-routing-18(仅 MVC 不带 Razor Pages) 等示例形成对照;
  2. 中间件注册:app.UseMiddleware<GreeterMiddleware>()将自定义中间件插入管线。由于.NET 10的WebApplication会自动在管线前段加入路由中间件,因此中间件内调用GetEndpoint()时,路由匹配已完成,可以拿到当前端点;
  3. 端点映射:app.MapRazorPages()把Pages目录下的 Razor Page 映射为端点。中间件注册在端点映射之前,这并不影响GetEndpoint()的使用——端点路由中间件已经提前完成了匹配;
  4. 构造函数注入:中间件通过构造函数注入了LinkGenerator(用于在端点路由中生成链接的服务)。本例的InvokeAsync中并未实际使用它,属于示例保留字段;真正起作用的是_next委托与GetEndpoint()的调用;
  5. 端点分流:endPoint.DisplayName == "/about"判断当前端点是否为 about 页面。命中时向HttpContext.Items写入键"GreetingFromMiddleWare",值为"Hello world from GreetingMiddleware";
  6. 继续转发:await _next.Invoke(httpContext)将请求交给管线后续环节(此处即 Razor Pages 端点执行器)。

⚠️ 空值防护提醒:示例代码直接访问endPoint.DisplayName而未判空。在生产代码中,GetEndpoint()在请求未匹配任何端点时返回null,推荐写成:

var endpoint = httpContext.GetEndpoint(); if (endpoint?.DisplayName == "/about") { // 仅当命中 /about 端点时执行的逻辑 }

页面层:问候语如何被渲染出来

首页 index.cshtml

index.cshtml 只做一件事:给出指向/about的入口链接,便于人工验证中间件行为:

@page <html> <body> <h1>An example of accessing Endpoint class from a middleware</h1> <ul> <li><a href="/about">/about</a></li> </ul> </body> </html>

访问首页根路径/时,路由命中的是 index 页面端点,其DisplayName不是"/about",因此中间件不会写入问候语——这正是"按端点分流"的直观体现:同一中间件对不同端点表现出不同行为。

about 页面 about.cshtml

about.cshtml 从HttpContext.Items读取中间件写入的值并渲染:

@page <html> <body> <h2>@HttpContext.Items["GreetingFromMiddleWare"]</h2> </body> </html>

HttpContext.Items是一个IDictionary<object, object?>,存活于单次请求生命周期内,非常适合中间件与页面/控制器之间传递轻量数据。

运行验证

在项目目录下执行:

dotnet run

然后依次访问:

  • http://localhost:5000/→ 首页无问候语;
  • http://localhost:5000/about→ 页面渲染出Hello world from GreetingMiddleware,证明中间件成功通过GetEndpoint()识别出了/about端点并写入了数据。

(具体端口以dotnet run实际输出为准。)

另一种等价实现:通过 IEndpointFeature 获取 Endpoint

GetEndpoint()本质上是特性的"语法糖"。仓库中的 new-routing-14 示例 展示了等价的底层写法:

using Microsoft.AspNetCore.Http.Features; var builder = WebApplication.CreateBuilder(); builder.Services.AddControllersWithViews(); var app = builder.Build(); app.MapGet("/", async context => { var feature = context.Features.Get<IEndpointFeature>(); await context.Response.WriteAsync($"Endpoint Name {feature.Endpoint.DisplayName}"); }); app.Run();

两种方式的关系:

  • context.Features.Get<IEndpointFeature>()直接读取特性集合中的IEndpointFeature,随后访问其Endpoint属性;
  • httpContext.GetEndpoint()是封装了上述读取过程的扩展方法,代码更简洁;
  • 两者获取到的是同一个Endpoint实例。当没有匹配端点时,GetEndpoint()返回null,而IEndpointFeature同样可能为null或其Endpoint为null,使用前都需要判空。

从源码结构看,GetEndpoint()的实现即是对Features.Get<IEndpointFeature>()?.Endpoint的简化包装——这也是它在 README 中被冠以 "brand new"(当时新引入)扩展方法的原因。

进阶:从 Endpoint 读取 Metadata,而不是硬编码 DisplayName

示例中通过比较DisplayName字符串来决定分支,这在端点较少时直观可用,但字符串比较脆弱(端点显示名可能因映射方式不同而变化)。更稳健的做法是从Endpoint.Metadata读取自定义元数据。

仓库中的 new-routing-11 示例 展示了这一模式:定义一个MessageAttribute,在中间件中通过endPoint.Metadata.GetMetadata<MessageAttribute>()取出,若存在则写入问候语:

Endpoint endPoint = httpContext.GetEndpoint(); var message = endPoint.Metadata.GetMetadata<MessageAttribute>(); if (message != null) { httpContext.Items.Add("GreetingFromMiddleWare", message.Content); } await _next.Invoke(httpContext);

两者对比:

方案判定方式优点局限
new-routing-10DisplayName == "/about"代码最少,直观看懂字符串耦合,端点改名即失效
new-routing-11Metadata.GetMetadata<T>()类型安全、可携带任意数据需要先定义 Attribute 并注册到端点

将两者结合是最佳实践:先用GetEndpoint()拿到端点,再用Endpoint.Metadata做类型化的"按端点定制行为",这也是仓库中 new-routing-12(Razor Page 按请求方法取元数据) 与 new-routing-13(MVC Controller 端点元数据) 继续深化的方向。

使用建议与注意事项

  1. 判空是必须的:GetEndpoint()可能返回null(未匹配任何端点时),示例代码为保持精简未判空,生产代码务必加上;
  2. 调用时机:只有请求经过端点路由中间件完成匹配后,GetEndpoint()才能取到值。在WebApplication(Minimal Hosting)模板下路由中间件自动装配,自定义中间件位于其后即可正常使用;若使用传统的IApplicationBuilder显式装配,需确保app.UseRouting()在自定义中间件之前执行;
  3. DisplayName 的来源:Razor Page 的DisplayName与其@page路由模板相关(@page无参时即页面路径,如/about);MVC 端点的DisplayName则形如ControllerName.ActionName。跨类型比较时要留意差异;
  4. 中间件顺序影响行为:若把UseMiddleware<GreeterMiddleware>()放在会短路请求的中间件之后,可能永远执行不到GetEndpoint()的分支逻辑;
  5. 与端点执行器的关系:GetEndpoint()只是"读取匹配结果",并不会改变端点路由的执行流程;真正执行业务逻辑仍需await _next.Invoke(httpContext)将控制权交给后续管线(本例中即 Razor Pages 端点)。

总结

HttpContext.GetEndpoint()是端点路由模型中连接"中间件世界"与"路由世界"的关键桥梁。通过 new-routing-10 这个最小示例,你可以看到一条清晰的实践路径:

  1. 注册中间件 → 2. 在InvokeAsync中调用httpContext.GetEndpoint()→ 3. 依据DisplayName或Metadata判定当前端点 → 4. 写入HttpContext.Items等共享数据 → 5. 转发请求让端点执行。

结合仓库中的 new-routing-11(元数据方案)、new-routing-14(IEndpointFeature底层实现)、new-routing-12 与 new-routing-13(Razor Page / MVC 元数据进阶),你可以把这一技巧从"示例"扩展为"面向生产的分端点中间件设计模式"——无论是做按端点注入响应头、权限预处理,还是针对特定页面注入统计逻辑,GetEndpoint()都是那个正确的起点。

  • 示例工程

【免费下载链接】practical-aspnetcore

Practical samples of ASP.NET Core 11, 10, 9, 8.0, 7.0, 6.0, 5.0, 3.1, 2.2, and 2.1,projects you can use. Readme contains explanations on all projects.

项目地址:https://gitcode.com/gh_mirrors/pr/practical-aspnetcore
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 17:46:11

雷达工程数据可视化与处理:从二进制到距离-多普勒图与CFAR检测

简介&#xff1a;这份资源面向雷达工程、探地雷达&#xff08;GPR&#xff09;信号处理方向的学习者与工程人员&#xff0c;聚焦高速路数据采集场景下的雷达数据可视化与处理&#xff0c;帮助读者理解如何将原始回波信号转换为可判读的雷达剖面图&#xff0c;并完成地下结构、空…

作者头像 李华
网站建设 2026/10/15 17:45:15

蓝桥杯C51备赛杂记:单片机外设开发与避坑指南

备赛蓝桥杯单片机方向的时候&#xff0c;我陆续整理了不少关于C51的笔记&#xff0c;零零散散写在草稿纸、手机备忘和电脑代码注释里&#xff0c;后来干脆整理成一份“杂记”&#xff0c;边写边改&#xff0c;最后成了自己的复习手册。这篇博文就是从那本杂记里挑出来的部分内容…

作者头像 李华
网站建设 2026/10/15 17:44:56

Qt TCP通信实战:解决粘包、断线重连与事件循环坑点

简介&#xff1a;本资源是一份基于Qt框架实现TCP客户端与服务器双向通信的完整示例工程&#xff0c;面向C与Qt初学者、嵌入式/桌面应用开发学习者&#xff0c;解决网络编程中连接建立、数据收发、GUI集成及异常处理等核心实践问题。压缩包共8个文件&#xff0c;含3个关键cpp源码…

作者头像 李华
网站建设 2026/10/15 17:43:35

智能世界2035技术趋势解读:AGI、AI Agent与算力能源协同落地实践

简介&#xff1a;这份《智能世界2035》是华为发布的面向人工智能、物联网、算力与能源等领域从业者及研究者的前瞻性报告&#xff0c;聚焦未来十年AI如何从数字世界迈向物理世界&#xff0c;并系统梳理医疗、教育、智能工厂与农场、智慧城市、智能家居及商业创新等场景的变革路…

作者头像 李华