自定义异常处理器中GetEndpoint为Null且RouteValues为空的问题排查
问题原因
你当前使用的UseExceptionHandler默认会在捕获异常后创建一个新的HttpContext生成错误响应,这个新上下文不包含原请求的Endpoint和RouteValues信息,因此你获取到的是空值。此外,你没有使用存储原请求路由信息的IExceptionHandlerPathFeature接口,导致无法拿到原请求的路由数据。
解决方案
方案一:改用自定义异常中间件(推荐)
直接编写包含try/catch的自定义中间件,在原请求上下文上处理异常,可直接访问原请求的Endpoint和RouteValues:
public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder app) { return app.Use(async (context, next) => { try { await next(); // 处理404状态码(可选) if (context.Response.StatusCode == StatusCodes.Status404NotFound && !context.Response.HasStarted) { var problemDetails = new ProblemDetails { Status = StatusCodes.Status404NotFound, Title = "资源未找到", Detail = $"请求路径 {context.Request.Path} 不存在", Instance = context.Request.Path }; AppendRouteAndEndpointInfo(context, problemDetails); context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(problemDetails); } } catch (Exception ex) { var problemDetails = new ProblemDetails { Status = StatusCodes.Status500InternalServerError, Title = "发生未处理异常", Detail = ex.Message, Instance = context.Request.Path }; AppendRouteAndEndpointInfo(context, problemDetails); if (!context.Response.HasStarted) { context.Response.Clear(); context.Response.StatusCode = problemDetails.Status.Value; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(problemDetails); } } }); void AppendRouteAndEndpointInfo(HttpContext ctx, ProblemDetails details) { var endpoint = ctx.GetEndpoint(); var routeValues = ctx.Features.Get<IRouteValuesFeature>()?.RouteValues; if (routeValues != null && routeValues.Any()) { details.Extensions["RouteValues"] = routeValues; } if (endpoint != null) { details.Extensions["Endpoint"] = endpoint.DisplayName; } } }
调整管道注册顺序,将该中间件放在Cors之后、其他业务中间件之前:
var app = builder.Build(); app.UseCors("OurPolicy"); // 注册自定义异常中间件 app.UseCustomExceptionHandler(); app.UseStatusCodePages(); app.UseAuthentication(); app.UseAuthorization(); app.AddPeopleEndpoints(); await app.RunAsync();
方案二:基于UseExceptionHandler改造(兼容原有逻辑)
如果坚持使用UseExceptionHandler,可通过IExceptionHandlerPathFeature获取原请求路由值,并通过上下文Items存储原Endpoint:
- 添加中间件存储原请求的
Endpoint:
public static IApplicationBuilder UseStoreOriginalEndpoint(this IApplicationBuilder app) { return app.Use(async (context, next) => { await next(); var endpoint = context.GetEndpoint(); if (endpoint != null) { context.Items["OriginalEndpoint"] = endpoint; } }); }
- 修改异常处理器逻辑:
public static IApplicationBuilder UseExceptionHandlerMiddleware(this IApplicationBuilder app) => app.UseExceptionHandler(new ExceptionHandlerOptions { AllowStatusCode404Response = true, ExceptionHandler = async context => { var exceptionPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var exception = exceptionPathFeature?.Error; // 获取原请求路由值 var routeValues = exceptionPathFeature?.RouteValues; // 获取原请求Endpoint var originalContext = context.Features.Get<IExceptionHandlerFeature>()?.Context; var endpoint = originalContext?.Items["OriginalEndpoint"] as Endpoint; var problemDetails = new ProblemDetails { Status = StatusCodes.Status500InternalServerError, Title = "发生未处理异常", Detail = exception?.Message, Instance = exceptionPathFeature?.Path }; if (routeValues != null && routeValues.Any()) { problemDetails.Extensions["RouteValues"] = routeValues; } if (endpoint != null) { problemDetails.Extensions["Endpoint"] = endpoint.DisplayName; } context.Response.StatusCode = problemDetails.Status.Value; context.Response.ContentType = "application/problem+json"; await context.Response.WriteAsJsonAsync(problemDetails); } });
- 调整管道顺序:
var app = builder.Build(); app.UseCors("OurPolicy"); app.UseExceptionHandlerMiddleware(); app.UseStatusCodePages(); app.UseAuthentication(); app.UseAuthorization(); // 注册存储原Endpoint的中间件 app.UseStoreOriginalEndpoint(); app.AddPeopleEndpoints(); await app.RunAsync();
内容的提问来源于stack exchange,提问作者WBuck
相关产品推荐
相关产品推荐

