在C# ASP.NET Core中如何将中间件捕获的异常重定向回控制器方法?
问题分析与解决方案
你遇到的核心问题是重定向会触发浏览器发起全新的HTTP请求,原请求的HttpContext.Features不会被携带到新请求中,因此ExceptionManagerController拿到的是新请求的HttpContext,自然找不到你设置的IExceptionHttpContextFeature。
以下是几种可行的解决思路:
方案1:用Session传递异常信息
Session适合跨请求传递数据,能满足重定向场景的需求。注意直接存储Exception对象可能存在序列化问题,建议存储异常的关键信息,或让自定义异常实现ISerializable接口。
修改中间件代码
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { if (exception != null) { Logger.LogError($"Erro na solicitação do usuário {httpContext.User.Identity.Name}. Error Message: {exception.Message}, Time of occurrence {DateTime.UtcNow}"); // 将异常关键信息存入Session httpContext.Session.SetString("ExceptionMessage", exception.Message); httpContext.Session.SetString("ExceptionType", exception.GetType().FullName); httpContext.Response.Redirect("/GestorDeExcecoes/Index"); } return true; }
修改控制器代码
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public async Task<IActionResult> Index() { var exceptionMessage = HttpContext.Session.GetString("ExceptionMessage"); var exceptionType = HttpContext.Session.GetString("ExceptionType"); if (string.IsNullOrEmpty(exceptionMessage)) { return RedirectToAction("Index", "Home"); } else { // 构建视图模型,可根据类型自定义处理逻辑 var vm = new GlobalExceptionVM(new Exception(exceptionMessage) { Source = exceptionType }); // 清理Session,避免重复读取 HttpContext.Session.Remove("ExceptionMessage"); HttpContext.Session.Remove("ExceptionType"); return View(vm); } }
启用Session
需要在Program.cs中注册并启用Session:
builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(1); // 设置过期时间 options.Cookie.IsEssential = true; }); // ... app.UseSession();
方案2:直接返回错误视图(无需重定向)
如果不需要跳转URL,直接在中间件中调用错误视图,复用同一个HttpContext,就能正常读取到你设置的Feature。
修改中间件代码
private readonly IServiceProvider _serviceProvider; public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger, IServiceProvider serviceProvider) { Logger = logger; _serviceProvider = serviceProvider; } public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { if (exception != null) { Logger.LogError($"Erro na solicitação do usuário {httpContext.User.Identity.Name}. Error Message: {exception.Message}, Time of occurrence {DateTime.UtcNow}"); // 设置Feature httpContext.Features.Set<IExceptionHttpContextFeature>(new ExceptionHttpContextFeature(exception)); // 获取控制器实例并调用视图 var controller = _serviceProvider.GetRequiredService<ExceptionManagerController>(); controller.ControllerContext = new ControllerContext { HttpContext = httpContext }; var result = await controller.Index(); await result.ExecuteResultAsync(controller.ControllerContext); } return true; }
方案3:用加密Cookie传递关键信息
如果需要跨请求传递少量数据,可以将异常信息加密后存入Cookie,控制器读取后立即删除Cookie。这种方式适合存储简短的异常描述,不适合传递完整异常对象。
额外优化建议
- 移除
ExceptionHttpContextFeature的无参构造函数,强制传入异常,避免空引用:
public class ExceptionHttpContextFeature: IExceptionHttpContextFeature { public Exception Exception { get; } public ExceptionHttpContextFeature(Exception exception) { Exception = exception ?? throw new ArgumentNullException(nameof(exception)); } }
- 确保
IExceptionHandler已正确注册到DI容器:
builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); app.UseExceptionHandler(); // 启用异常处理中间件
内容的提问来源于stack exchange,提问作者Philipe Riskalla Leal
相关产品推荐
相关产品推荐

