如何处理 .net MVC Core 中的动态错误页面?

Posted

技术标签:

【中文标题】如何处理 .net MVC Core 中的动态错误页面?【英文标题】:How to handle dynamic error pages in .net MVC Core? 【发布时间】:2020-07-03 23:34:05 【问题描述】:

目前我有

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()。

【问题讨论】:

我不确定这是否会帮助docs.microsoft.com/en-us/aspnet/core/fundamentals/… 我可能只是在默认异常处理程序调用的错误方法中处理重定向(假设您对每个租户都有一个错误页面),或者您可以像上面的链接所示和动态创建响应。 错误处理程序做了很多开箱即用的事情,比如传递错误代码等等。如果仍然拥有它并且能够动态设置 url,那就太好了。 您的应用程序是否监听 Tenant1/Home/Error 和 Tenant2/Home/Error? 是的。但问题是路由它 【参考方案1】:

我们假设应用程序需要/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(我们在上述解决方案中假设的),您可以编写一个中间件来处理异常,方法如下:

Configure 方法中的 Startup.cs 中添加中间件:

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&lt;Exception&gt;(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 );

请注意,设置在中间件上的对象现在可以检索了。

【讨论】:

HttpContext.Features.Get() 用于在错误控制器中可用。使用重定向会丢失该上下文,您将无法再检索它。 @Murdock 第二个解决方案怎么样?第二种解决方案似乎有效。 我的租户是动态的,因此我无法静态添加路由。 路由app.UseExceptionHandler($"/tenant/Home/Error"); 不是静态的!你可以放任何你想要的而不是tenant,并且使用Route("/TenantId/Home/Error")]的路由你可以听所有的。 好的,但是 .net 如何知道使用 app.UseExceptionHandler($"/tenant/Home/Error") 路由到哪个租户。问题询问如何从引发错误的传入 url 推断它。?

以上是关于如何处理 .net MVC Core 中的动态错误页面?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理动态IP地址mvc 4

ASP.NET Core Web API - 如何处理 URL 查询字符串中的“null”与“undefined”?

.NET Core 依赖注入 - 如何处理手动创建的实例?

如何处理 ASP.NET MVC 3 中每个键的多个值?

我应该如何处理 Window Phone / WCF / ASP.NET MVC 应用程序的用户身份?

asp.net mvc 中模型验证如何处理数组呢?