Wie gehe ich mit dynamischen Fehlerseiten in .net MVC Core um?

8

Derzeit habe ich

app.UseExceptionHandler("/Home/Error");

Ich möchte den Pfad relativ zum ursprünglichen Pfad erstellen.

Zum Beispiel wenn

Tenant1 / PageThatThrowsError dann app.UseExceptionHandler ("Tenant1 / Home / Error");

doch wenn

Tenant2 / PageThatThrowsError dann app.UseExceptionHandler ("Tenant2 / Home / Error");

Ich dachte, ich könnte es tun

app.UseExceptionHandler(
    new ExceptionHandlerOptions
    {
        ExceptionHandler = async (ctx) =>
        {
            //logic that extracts tenant
            ctx.Request.Path = new PathString(Invariant($"{tenant}/Home/Error"));
        }
    }
);

aber das wirft eine 500

BEARBEITEN: Alle aktuellen Lösungen, die beispielsweise Weiterleitungen verwenden, verlieren den aktuellen Fehlerkontext und erlauben dem Controller nicht, beispielsweise HttpContext.Features.Get () aufzurufen.

Murdock
quelle
Ich bin nicht sicher, ob dies docs.microsoft.com/en-us/aspnet/core/fundamentals/…
Patrick Mcvay
Ich würde wahrscheinlich nur die Umleitung (vorausgesetzt, Sie haben eine Fehlerseite für jeden Mandanten) in der Fehlermethode behandeln, die vom Standardausnahmehandler aufgerufen wird, oder Sie können wie oben gezeigt den Link anzeigen und die Antwort dynamisch erstellen.
Patrick Mcvay
Der Fehlerbehandler erledigt eine Reihe von Aufgaben wie das Übergeben des Fehlercodes und so weiter. Es wäre schön, das noch zu haben und die URL einfach dynamisch einstellen zu können.
Murdock
Hört Ihre Anwendung Tenant1 / Home / Error und Tenant2 / Home / Error?
Alireza Mahmoudi
Ja. Aber das Problem ist die Weiterleitung
Murdock

Antworten:

2

Wir nehmen an, dass die Anwendung Routen und Endpunkte von /Tenant1/Home/Errorund benötigt hat /Tenant2/Home/Error. Sie können das Problem mit diesem Code lösen:

app.UseExceptionHandler(
    new ExceptionHandlerOptions
    {
        ExceptionHandler = async (ctx) =>
        {
            string tenant = ctx.Request.Host.Value.Split('/')[0];
            ctx.Response.Redirect($"/{tenant}/Home/Error");
        },
    }
);

Eine andere äquivalente Lösung besteht darin, den folgenden Code auf das zu setzen startup.cs:

app.UseExceptionHandler("$/{tenant}/Home/Error");

Wir nehmen an, dass tenantdas von irgendwo wie Appsettings kommt. Dann können Sie leicht Ausnahmen für Ihren gewünschten Endpunkt erhalten, indem Sie eine einfache Route für Ihre Aktion schreiben:

[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 });
}

oder Sie können zwei verschiedene Aktionen erstellen:

[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 });
}

Aktualisieren:

Wenn Ihre Mandanten dynamisch hinzugefügt werden und nicht in Ihre appsettings.json(was wir in den obigen Lösungen angenommen haben) eingefügt werden können , können Sie eine Middleware schreiben, um die Ausnahmen zu behandeln.

Fügen Sie die Middleware zu Ihrer Startup.csIn- ConfigureMethode hinzu:

app.UseMiddleware(typeof(ErrorHandlingMiddleware));

Fügen Sie in der nächsten Zeile eine Route für Fehler hinzu (genau nach der Middleware):

app.UseMvc(routes =>
    {
       routes.MapRoute(
            name: "errors",
            template: "{tenant}/{controller=Home}/{action=Index}/");
    });

Erstellen Sie eine Klasse für Ihre Middleware und setzen Sie diesen Code auf:

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);
    }
}

Beachten Sie, dass Sie dem Kontext Folgendes hinzufügen können : context.Request.HttpContext.Features.Set<Exception>(ex);.

Und schließlich sollten Sie eine Aktion mit einem geeigneten Routing erstellen, um Ihre Logik dort zu schreiben:

[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 });
}

Beachten Sie, dass das Objekt, das in der Middleware festgelegt wurde, jetzt abgerufen werden kann.

Alireza Mahmoudi
quelle
HttpContext.Features.Get <IExceptionHandlerPathFeature> () wird verwendet, um im Fehlercontroller verfügbar zu sein. Wenn Sie die Umleitung verwenden, wird dieser Kontext verloren, und Sie können ihn nicht mehr abrufen.
Murdock
@ Murdock Was ist mit der zweiten Lösung? Die zweite Lösung scheint zu funktionieren.
Alireza Mahmoudi
Meine Mieter sind dynamisch, daher kann ich die Route nicht statisch hinzufügen.
Murdock
Die Route app.UseExceptionHandler($"/{tenant}/Home/Error");ist nicht statisch! Sie können setzen, was immer Sie wollen, anstatt {tenant}, und mit der Route von Route("/{TenantId}/Home/Error")]Ihnen können Sie alle von ihnen hören.
Alireza Mahmoudi
Ok, aber woher weiß .net, an welchen Mandanten mit app.UseExceptionHandler ($ "/ {Tenant} / Home / Error") weitergeleitet werden soll. Die Frage fragt, wie sie aus der eingehenden URL abgeleitet werden kann, die den Fehler ausgelöst hat.
Murdock