基于HttpContext.SetEndpoint的.NET Core请求重写中间件实现问题
.NET Core 中间件重写请求路径到指定Action的正确实现
为什么修改路径后没生效?
ASP.NET Core的路由匹配是在UseRouting中间件阶段完成的,如果在路由匹配之后修改Request.Path,不会触发重新路由。另外SetEndpoint是直接指定已匹配的端点,而非修改路径,需要用正确的方式结合使用。
方法一:修改请求路径并触发重新路由
通过修改Request.Path并清除已有路由数据,让路由中间件重新匹配目标Action。
代码示例
// 自定义重写中间件,必须放在UseRouting之前 app.Use(async (context, next) => { // 匹配需要重写的原始路径 if (context.Request.Path.Equals("/old-path", StringComparison.OrdinalIgnoreCase)) { // 重写为目标Action的路径 context.Request.Path = "/Home/TargetAction"; // 清除路由数据,强制路由中间件重新匹配 var routingFeature = context.Features.Get<IRoutingFeature>(); if (routingFeature?.RouteData != null) { routingFeature.RouteData.Values.Clear(); } } await next(); }); // 路由中间件,必须在重写中间件之后 app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
方法二:直接用SetEndpoint指定目标Action
通过获取目标Action的描述符,直接创建并设置Endpoint,跳过路由匹配流程。
代码示例
- 先确保注入Action描述符服务(Program.cs中):
builder.Services.AddSingleton<IActionDescriptorCollectionProvider, ActionDescriptorCollectionProvider>();
- 自定义中间件:
app.Use(async (context, next) => { if (context.Request.Path.Equals("/old-path", StringComparison.OrdinalIgnoreCase)) { var actionProvider = context.RequestServices.GetRequiredService<IActionDescriptorCollectionProvider>(); // 定位目标Action(替换为你的控制器和Action名称) var targetAction = actionProvider.ActionDescriptors.Items .OfType<ControllerActionDescriptor>() .FirstOrDefault(ad => ad.ControllerName.Equals("Home", StringComparison.OrdinalIgnoreCase) && ad.ActionName.Equals("TargetAction", StringComparison.OrdinalIgnoreCase)); if (targetAction != null) { // 创建Endpoint实例 var endpoint = new Endpoint( _ => Task.CompletedTask, new EndpointMetadataCollection(targetAction), targetAction.DisplayName); // 设置Endpoint,后续流程将直接使用这个端点 context.SetEndpoint(endpoint); // 如需传递路由参数,可设置RouteData var routingFeature = context.Features.Get<IRoutingFeature>() ?? new RoutingFeature(); routingFeature.RouteData.Values["id"] = "123"; // 示例参数 context.Features.Set(routingFeature); } } await next(); }); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
关键注意事项
- 中间件顺序:重写逻辑必须放在
UseRouting之前,否则路由已完成匹配,修改不会生效。 SetEndpoint需要确保目标Action存在,否则会返回404。- 传递参数时,需同步更新
RouteData的值,保证Action能正确接收参数。
内容的提问来源于stack exchange,提问作者Aashutosh
相关产品推荐
相关产品推荐

