it-swarm.com.de

Benutzerdefinierte ASP.NET MVC-Fehlerbehandlung Application_Error Global.asax?

Ich habe etwas Basiscode, um Fehler in meiner MVC-Anwendung zu ermitteln. Derzeit habe ich in meinem Projekt einen Controller namens Error mit den Aktionsmethoden HTTPError404(), HTTPError500() und General(). Sie akzeptieren alle einen String-Parameter error. Verwenden oder Ändern des folgenden Codes ... Was ist der beste/richtige Weg, um die Daten zur Verarbeitung an den Error-Controller zu übergeben? Ich hätte gerne eine möglichst robuste Lösung.

protected void Application_Error(object sender, EventArgs e)
{
    Exception exception = Server.GetLastError();
    Response.Clear();

    HttpException httpException = exception as HttpException;
    if (httpException != null)
    {
        RouteData routeData = new RouteData();
        routeData.Values.Add("controller", "Error");
        switch (httpException.GetHttpCode())
        {
            case 404:
                // page not found
                routeData.Values.Add("action", "HttpError404");
                break;
            case 500:
                // server error
                routeData.Values.Add("action", "HttpError500");
                break;
            default:
                routeData.Values.Add("action", "General");
                break;
        }
        routeData.Values.Add("error", exception);
        // clear error on server
        Server.ClearError();

        // at this point how to properly pass route data to error controller?
    }
}
97
aherrick

Anstatt dafür eine neue Route anzulegen, können Sie einfach zu Ihrem Controller/Ihrer Aktion umleiten und die Informationen per Abfragestring weiterleiten. Zum Beispiel:

protected void Application_Error(object sender, EventArgs e) {
  Exception exception = Server.GetLastError();
  Response.Clear();

  HttpException httpException = exception as HttpException;

  if (httpException != null) {
    string action;

    switch (httpException.GetHttpCode()) {
      case 404:
        // page not found
        action = "HttpError404";
        break;
      case 500:
        // server error
        action = "HttpError500";
        break;
      default:
        action = "General";
        break;
      }

      // clear error on server
      Server.ClearError();

      Response.Redirect(String.Format("~/Error/{0}/?message={1}", action, exception.Message));
    }

Dann erhält Ihr Controller, was Sie wollen:

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}

Es gibt einige Kompromisse bei Ihrem Ansatz. Seien Sie sehr vorsichtig beim Schleifen dieser Art der Fehlerbehandlung. Eine andere Sache ist, dass Sie ein Session-Objekt für all diese Treffer erstellen, da Sie durch die asp.net-Pipeline gehen, um einen 404-Prozessor zu verarbeiten. Dies kann bei stark genutzten Systemen ein Problem (Leistung) sein.

99
andrecarlucci

Um die erste Frage zu beantworten, "Wie werden Routedaten korrekt an den Fehlercontroller übergeben?"

IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));

Implementieren Sie dann in Ihrer ErrorController-Klasse eine Funktion wie folgt:

[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Error(Exception exception)
{
    return View("Error", exception);
}

Dadurch wird die Ausnahme in die Ansicht verschoben. Die Ansichtsseite sollte wie folgt deklariert werden:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<System.Exception>" %>

Und den Code, um den Fehler anzuzeigen:

<% if(Model != null) { %>  <p><b>Detailed error:</b><br />  <span class="error"><%= Helpers.General.GetErrorMessage((Exception)Model, false) %></span></p> <% } %>

Hier ist die Funktion, die alle Ausnahmemeldungen aus dem Ausnahmebaum erfasst:

    public static string GetErrorMessage(Exception ex, bool includeStackTrace)
    {
        StringBuilder msg = new StringBuilder();
        BuildErrorMessage(ex, ref msg);
        if (includeStackTrace)
        {
            msg.Append("\n");
            msg.Append(ex.StackTrace);
        }
        return msg.ToString();
    }

    private static void BuildErrorMessage(Exception ex, ref StringBuilder msg)
    {
        if (ex != null)
        {
            msg.Append(ex.Message);
            msg.Append("\n");
            if (ex.InnerException != null)
            {
                BuildErrorMessage(ex.InnerException, ref msg);
            }
        }
    }
27
user248913

Ich habe eine Lösung für Ajax-Problem gefunden, die von Lion_cl notiert wurde.

global.asax:

protected void Application_Error()
    {           
        if (HttpContext.Current.Request.IsAjaxRequest())
        {
            HttpContext ctx = HttpContext.Current;
            ctx.Response.Clear();
            RequestContext rc = ((MvcHandler)ctx.CurrentHandler).RequestContext;
            rc.RouteData.Values["action"] = "AjaxGlobalError";

            // TODO: distinguish between 404 and other errors if needed
            rc.RouteData.Values["newActionName"] = "WrongRequest";

            rc.RouteData.Values["controller"] = "ErrorPages";
            IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
            IController controller = factory.CreateController(rc, "ErrorPages");
            controller.Execute(rc);
            ctx.Server.ClearError();
        }
    }

ErrorPagesController

public ActionResult AjaxGlobalError(string newActionName)
    {
        return new AjaxRedirectResult(Url.Action(newActionName), this.ControllerContext);
    }

AjaxRedirectResult

public class AjaxRedirectResult : RedirectResult
{
    public AjaxRedirectResult(string url, ControllerContext controllerContext)
        : base(url)
    {
        ExecuteResult(controllerContext);
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context.RequestContext.HttpContext.Request.IsAjaxRequest())
        {
            JavaScriptResult result = new JavaScriptResult()
            {
                Script = "try{history.pushState(null,null,window.location.href);}catch(err){}window.location.replace('" + UrlHelper.GenerateContentUrl(this.Url, context.HttpContext) + "');"
            };

            result.ExecuteResult(context);
        }
        else
        {
            base.ExecuteResult(context);
        }
    }
}

AjaxRequestExtension

public static class AjaxRequestExtension
{
    public static bool IsAjaxRequest(this HttpRequest request)
    {
        return (request.Headers["X-Requested-With"] != null && request.Headers["X-Requested-With"] == "XMLHttpRequest");
    }
}
9

Ich hatte vorher Probleme mit der Idee, eine globale Fehlerbehandlungsroutine in einer MVC-App zu zentralisieren. Ich habe einen Beitrag in den ASP.NET-Foren .

Es behandelt im Wesentlichen alle Anwendungsfehler in der Datei global.asax, ohne dass ein Fehler-Controller erforderlich ist, der mit dem [HandlerError]-Attribut verziert wird oder mit dem customErrors-Knoten in der web.config-Datei getüftelt wird. 

9
Jack Hsu

Eine bessere Möglichkeit, Fehler in MVC zu behandeln, besteht möglicherweise darin, das HandleError-Attribut auf Ihren Controller oder Ihre Aktion anzuwenden und die Shared/Error.aspx-Datei zu aktualisieren, um das zu tun, was Sie möchten. Das Model-Objekt auf dieser Seite enthält eine Exception-Eigenschaft sowie ControllerName und ActionName.

6
Brian

Application_Error hat ein Problem mit Ajax-Anforderungen. Wenn ein Fehler in Aktion behandelt wurde, der von Ajax aufgerufen wurde, wird die Fehleransicht im resultierenden Container angezeigt.

4

Brian,. Diese Vorgehensweise eignet sich hervorragend für Anfragen, die nicht von Ajax stammen. Wie Lion_cl jedoch angibt, wird bei einem Ajax-Aufruf eine Share/Error.aspx-Ansicht (oder Ihre benutzerdefinierte Fehlerseitenansicht) an die Ajax-Anrufer - Der Benutzer wird NICHT zur Fehlerseite weitergeleitet.

3
coderob

Dies ist möglicherweise nicht der beste Weg für MVC ( https://stackoverflow.com/a/9461386/5869805 )

Im Folgenden wird beschrieben, wie Sie eine Ansicht in Application_Error rendern und in die http-Antwort schreiben. Sie müssen keine Weiterleitung verwenden. Dadurch wird eine zweite Anfrage an den Server verhindert, sodass der Link in der Adressleiste des Browsers gleich bleibt. Das kann gut oder schlecht sein, es hängt davon ab, was Sie wollen.

Global.asax.cs

protected void Application_Error()
{
    var exception = Server.GetLastError();
    // TODO do whatever you want with exception, such as logging, set errorMessage, etc.
    var errorMessage = "SOME FRIENDLY MESSAGE";

    // TODO: UPDATE BELOW FOUR PARAMETERS ACCORDING TO YOUR ERROR HANDLING ACTION
    var errorArea = "AREA";
    var errorController = "CONTROLLER";
    var errorAction = "ACTION";
    var pathToViewFile = $"~/Areas/{errorArea}/Views/{errorController}/{errorAction}.cshtml"; // THIS SHOULD BE THE PATH IN FILESYSTEM RELATIVE TO WHERE YOUR CSPROJ FILE IS!

    var requestControllerName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["controller"]);
    var requestActionName = Convert.ToString(HttpContext.Current.Request.RequestContext?.RouteData?.Values["action"]);

    var controller = new BaseController(); // REPLACE THIS WITH YOUR BASE CONTROLLER CLASS
    var routeData = new RouteData { DataTokens = { { "area", errorArea } }, Values = { { "controller", errorController }, {"action", errorAction} } };
    var controllerContext = new ControllerContext(new HttpContextWrapper(HttpContext.Current), routeData, controller);
    controller.ControllerContext = controllerContext;

    var sw = new StringWriter();
    var razorView = new RazorView(controller.ControllerContext, pathToViewFile, "", false, null);
    var model = new ViewDataDictionary(new HandleErrorInfo(exception, requestControllerName, requestActionName));
    var viewContext = new ViewContext(controller.ControllerContext, razorView, model, new TempDataDictionary(), sw);
    viewContext.ViewBag.ErrorMessage = errorMessage;
    //TODO: add to ViewBag what you need
    razorView.Render(viewContext, sw);
    HttpContext.Current.Response.Write(sw);
    Server.ClearError();
    HttpContext.Current.Response.End(); // No more processing needed (ex: by default controller/action routing), flush the response out and raise EndRequest event.
}

Aussicht

@model HandleErrorInfo
@{
    ViewBag.Title = "Error";
    // TODO: SET YOUR LAYOUT
}
<div class="">
    ViewBag.ErrorMessage
</div>
@if(Model != null && HttpContext.Current.IsDebuggingEnabled)
{
    <div class="" style="background:Khaki">
        <p>
            <b>Exception:</b> @Model.Exception.Message <br/>
            <b>Controller:</b> @Model.ControllerName <br/>
            <b>Action:</b> @Model.ActionName <br/>
        </p>
        <div>
            <pre>
                @Model.Exception.StackTrace
            </pre>
        </div>
    </div>
}
2
burkay

Verwenden Sie folgenden Code für die Umleitung auf der Routenseite . Exception verwenden. Die Abfragezeichenfolge für die Coz-Ausnahmebedingung gibt einen Fehler aus, wenn die Abfragestringlänge verlängert wird.

routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);
0
Swapnil Malap