ASP.NET Core 8中IExceptionHandler无跳转显示错误页面问询
在ASP.NET Core 8 MVC中用
IExceptionHandler渲染HTML错误页(无重定向) 实现思路
核心是通过IRazorViewEngine直接渲染指定的Error视图为HTML字符串,再写入响应流,避免重定向导致地址栏变更。需要手动构建视图渲染所需的上下文,并注入必要的服务完成视图解析与渲染。
步骤1:注册必要服务
在Program.cs中添加异常处理器、上下文访问器等服务:
var builder = WebApplication.CreateBuilder(args); // 注册MVC服务 builder.Services.AddControllersWithViews(); // 注册自定义异常处理器 builder.Services.AddExceptionHandler<CustomExceptionHandler>(); // 注册HttpContext访问器(用于获取当前请求上下文) builder.Services.AddHttpContextAccessor(); // 注册日志服务(用于记录异常详情) builder.Services.AddLogging(); var app = builder.Build(); // 启用异常处理中间件 app.UseExceptionHandler(); // 其他中间件配置(静态文件、路由、授权等) app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
步骤2:实现自定义IExceptionHandler
创建CustomExceptionHandler类,完成异常日志记录、视图渲染与响应输出:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Diagnostics; using System.Text; public class CustomExceptionHandler : IExceptionHandler { private readonly IRazorViewEngine _razorViewEngine; private readonly ITempDataProvider _tempDataProvider; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger<CustomExceptionHandler> _logger; public CustomExceptionHandler( IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IHttpContextAccessor httpContextAccessor, ILogger<CustomExceptionHandler> logger) { _razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _httpContextAccessor = httpContextAccessor; _logger = logger; } public async ValueTask<bool> TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { // 记录异常详情(仅后台日志,不暴露给用户) _logger.LogError(exception, "发生未处理的服务器异常"); // 构建视图渲染所需的ActionContext var actionContext = new ActionContext( httpContext, httpContext.GetRouteData(), new ActionDescriptor(), new ModelStateDictionary()); // 准备错误页模型(与Home/Error视图的模型一致) var errorModel = new ErrorViewModel { RequestId = httpContext.TraceIdentifier, Message = "若问题持续,请联系管理员" }; // 查找Error视图(优先找Home控制器下的视图,其次Shared目录) var viewResult = _razorViewEngine.FindView(actionContext, "Error", isMainPage: false); if (!viewResult.Success) { // 视图不存在时返回简易错误HTML await httpContext.Response.WriteAsync( "<h1>服务器错误</h1><p>若问题持续,请联系管理员</p>", cancellationToken); return true; } // 渲染视图为HTML字符串 var errorHtml = await RenderViewToStringAsync( actionContext, viewResult.View, errorModel, cancellationToken); // 设置响应头与内容 httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; httpContext.Response.ContentType = "text/html; charset=utf-8"; await httpContext.Response.WriteAsync(errorHtml, cancellationToken); return true; } // 私有方法:将视图渲染为字符串 private async Task<string> RenderViewToStringAsync( ActionContext actionContext, IView view, object model, CancellationToken cancellationToken) { using var stringWriter = new StringWriter(); var viewData = new ViewDataDictionary( new EmptyModelMetadataProvider(), actionContext.ModelState) { Model = model }; var viewContext = new ViewContext( actionContext, view, viewData, new TempDataDictionary(actionContext.HttpContext, _tempDataProvider), stringWriter, new HtmlHelperOptions()); await view.RenderAsync(viewContext); return stringWriter.ToString(); } }
步骤3:准备错误页模型与视图
- ErrorViewModel(与Home控制器的Error Action模型一致):
public class ErrorViewModel { public string? RequestId { get; set; } public string? Message { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); }
- Error视图(
Views/Home/Error.cshtml或Views/Shared/Error.cshtml):
@model ErrorViewModel @{ ViewData["Title"] = "错误"; } <div class="container"> <h1 class="text-danger">服务器错误</h1> <p class="lead">@Model.Message</p> @if (Model.ShowRequestId) { <p> <strong>请求ID:</strong> <code>@Model.RequestId</code> </p> } </div>
关键说明
- 避免重定向:直接将渲染后的HTML写入响应,浏览器地址栏保持原请求路径,刷新时会重新发起原请求而非停留在错误页。
- 上下文构建:手动创建
ActionContext确保视图渲染时能获取到当前请求的路由、模型状态等信息。 - 安全注意:仅向用户展示友好提示,异常详情通过日志记录,避免泄露敏感信息。
内容的提问来源于stack exchange,提问作者PBO
相关产品推荐
相关产品推荐

