c# - 如何处理 .net MVC Core 中的动态错误页面?
问题描述
目前我有
app.UseExceptionHandler("/Home/Error");
我想让路径相对于原始路径。
例如,如果
Tenant1/PageThatThrowsError 然后 app.UseExceptionHandler("Tenant1/Home/Error");
但如果
Tenant2/PageThatThrowsError 然后 app.UseExceptionHandler("Tenant2/Home/Error");
我以为我能做到
app.UseExceptionHandler(
new ExceptionHandlerOptions
{
ExceptionHandler = async (ctx) =>
{
//logic that extracts tenant
ctx.Request.Path = new PathString(Invariant($"{tenant}/Home/Error"));
}
}
);
但这会引发 500
编辑:例如使用重定向的所有当前解决方案都会丢失当前的错误上下文,并且不允许控制器例如调用 HttpContext.Features.Get()。
解决方案
我们假设应用程序需要 和 的路由和/Tenant1/Home/Error
端点/Tenant2/Home/Error
。您可以使用以下代码解决问题:
app.UseExceptionHandler(
new ExceptionHandlerOptions
{
ExceptionHandler = async (ctx) =>
{
string tenant = ctx.Request.Host.Value.Split('/')[0];
ctx.Response.Redirect($"/{tenant}/Home/Error");
},
}
);
另一个等效的解决方案是将以下代码放在startup.cs
:
app.UseExceptionHandler("$/{tenant}/Home/Error");
我们假设这tenant
来自 appsettings 之类的地方。然后,您可以通过在您的操作上编写一个简单的路由来轻松地在您想要的端点上获得异常:
[Route("/{TenantId}/Home/Error")]
public IActionResult Error(string TenantId)
{
string Id = TenantId;
// Here you can write your logic and decide what to do based on TenantId
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
或者您可以创建两个不同的操作:
[Route("/Tenant1/Home/Error")]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[Route("/Tenant2/Home/Error")]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
更新:
如果您的租户是动态添加的并且不能放入您的appsettings.json
(我们在上述解决方案中假设的),您可以编写一个中间件来处理异常,方法如下:
Startup.cs
在您的inConfigure
方法中添加中间件:
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
在下一行添加错误路由(正好在中间件之后):
app.UseMvc(routes =>
{
routes.MapRoute(
name: "errors",
template: "{tenant}/{controller=Home}/{action=Index}/");
});
为您的中间件创建一个类,并将这些代码放在:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex,this.next);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex, RequestDelegate next)
{
string tenant = "tenant1";//write your logic something like this: context.Request.Path.Value.Split('/')[0];
context.Request.Path = new PathString($"/{tenant}/Home/Error");
context.Request.HttpContext.Features.Set<Exception>(ex);// add any object you want to the context
return next.Invoke(context);
}
}
请注意,您可以像这样将任何您想要的内容添加到上下文中context.Request.HttpContext.Features.Set<Exception>(ex);
:
最后,您应该创建一个带有适当路由的操作,以便在那里编写您的逻辑:
[Route("/{TenantId}/Home/Error")]
public IActionResult Error(string TenantId)
{
string Id = TenantId;
var exception= HttpContext.Features.Get<Exception>();// you can get the object which was set on the middle-ware
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
请注意,现在可以检索在中间件上设置的对象。
推荐阅读
- c# - 控制台应用程序的打印方式与在线编译器不同
- github - How do I get my site to show up through github?
- javascript - 如何使用输入文件 API 在 CSS 背景 URL 属性中显示上传的图像 - vanilla Javascript
- mysql - 尽管值正确,但列的数据太长
- shiny - 在 group_by 中使用 input$radio 按钮作为参数的任何机会
- javascript - fs 错误:EISDIR:对目录的非法操作,读取
- oop - 如何实现功能共享相同逻辑的接口?
- python - 如何在 MacOS 上卸载 OpenCV?
- mysql - 插入或更新,但不确定我是否可以进行重复的密钥更新
- python - 当 x 和 y 表示为 1d 数组时,如何使用 matplotlib 创建 3d 曲面图?