ASP.NET MVC Niestandardowy błąd obsługi aplikacji Błąd Globalny.asax?

Mam trochę podstawowego kodu do określenia błędów w mojej aplikacji MVC. Obecnie w moim projekcie mam kontroler o nazwie Error z metodami akcji HTTPError404(), HTTPError500(), i General(). Wszystkie one akceptują parametr string error. Używanie lub modyfikowanie poniższego kodu. Jaki jest najlepszy / właściwy sposób przekazania danych do kontrolera błędów do przetworzenia? Chciałbym mieć solidne rozwiązanie, jak to możliwe.

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?
    }
}
Author: Oualid KTATA, 2009-07-23

10 answers

Zamiast tworzyć nową trasę do tego celu, możesz po prostu przekierować do kontrolera / akcji i przekazać informacje za pomocą querystring. Na przykład:

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

Wtedy twój kontroler otrzyma co tylko chcesz:

// GET: /Error/HttpError404
public ActionResult HttpError404(string message) {
   return View("SomeView", message);
}
Jest kilka kompromisów z twoim podejściem. Bądź bardzo ostrożny z zapętlaniem w tego rodzaju obsłudze błędów. Inną rzeczą jest to, że skoro przechodzisz przez asp.net pipeline aby obsłużyć 404, utworzysz obiekt sesji dla wszystkich tych trafień. To może stanowić problem (wydajność) dla mocno używanych systemów.
 97
Author: andrecarlucci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-05 18:01:49

Aby odpowiedzieć na początkowe pytanie "jak poprawnie przekazać routedata do kontrolera błędów?":

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

Następnie w klasie ErrorController zaimplementuj taką funkcję:

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

To przesuwa wyjątek do widoku. Strona widoku powinna być zadeklarowana w następujący sposób:

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

Oraz kod wyświetlający błąd:

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

Oto funkcja, która zbiera wszystkie komunikaty WYJĄTKÓW z drzewa WYJĄTKÓW:

    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);
            }
        }
    }
 26
Author: Tim Cooper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-05 18:10:27

Znalazłem rozwiązanie problemu ajax zauważonego przez Lion_cl.

Globalny.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
Author: Jozef Krchňavý,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-10 15:45:39

Wcześniej zmagałem się z ideą centralizacji globalnej procedury obsługi błędów w aplikacji MVC. Mam posta na ASP.NET Forum .

Zasadniczo obsługuje wszystkie błędy aplikacji w globalnym.asax bez potrzeby kontrolera błędów, dekorowania atrybutem [HandlerError] lub majstrowania z węzłem customErrors w sieci.config.

 9
Author: Jack Hsu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-03 19:06:12

Być może lepszym sposobem obsługi błędów w MVC jest zastosowanie atrybutu HandleError do kontrolera lub akcji i zaktualizowanie współdzielonego / błędu.plik aspx, aby zrobić to, co chcesz. Obiekt modelu na tej stronie zawiera właściwość Exception oraz nazwę kontrolera i nazwę akcji.

 6
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-12 21:30:54

Application_Error mający problem z żądaniami Ajax. Jeśli błąd obsłużony w akcji wywołanej przez Ajax-wyświetli Twój widok błędu wewnątrz wynikowego kontenera.

 4
Author: Victor Gelmutdinov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-18 21:35:57

Brian, To podejście działa świetnie dla żądań innych niż Ajax, ale jak stwierdził Lion_cl, jeśli masz błąd podczas wywołania Ajax, Twój Share / Error.widok aspx (lub niestandardowy widok strony błędu) zostanie zwrócony do wywołującego Ajax-użytkownik nie zostanie przekierowany na stronę błędu.

 3
Author: coderob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-13 00:07:25

To może nie być najlepszy sposób na MVC ( https://stackoverflow.com/a/9461386/5869805 )

Poniżej przedstawiamy jak renderować widok w Application_Error i zapisać go do odpowiedzi http. Nie musisz używać przekierowania. Spowoduje to zablokowanie drugiego żądania do serwera, więc link na pasku adresu przeglądarki pozostanie taki sam. To może być dobre lub złe, to zależy od tego, czego chcesz.

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.
}

Widok

@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
Author: burkay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:46

Użyj następującego kodu do przekierowania na stronie trasy. Użyj wyjątku.Wiadomość o wyjątku. Coz łańcuch zapytania wyjątku wyświetla błąd, jeśli wydłuża długość zapytania.

routeData.Values.Add("error", exception.Message);
// clear error on server
Server.ClearError();
Response.RedirectToRoute(routeData.Values);
 0
Author: Swapnil Malap,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-11 06:53:05

Mam problem z tym podejściem do obsługi błędów: W przypadku sieci.config:

<customErrors mode="On"/>

Obsługa błędu wyszukuje błąd widoku.shtml i kroku przepływu sterowania do Application_Error global.asax only after exception

System.InvalidOperationException: the view 'Error' or its master was not found lub no view engine obsługuje wyszukiwane lokalizacje. Na szukano lokalizacji w: ~/Views/home / Error.aspx ~ / Views / home / Error.ascx ~ / Views / Shared / Error.aspx ~ / Views / Shared / Error.ascx ~/Views / home / Error.cshtml ~ / Views / home / Error.vbhtml ~/Views / Shared / Error.cshtml ~ / Views / Shared / Error.vbhtml w System.Www.Mvc.ViewResult.FindView(controllercontext) ....................

Więc

 Exception exception = Server.GetLastError();
  Response.Clear();
  HttpException httpException = exception as HttpException;

HttpException jest zawsze null wtedy customErrors mode = " On" :( To wprowadza w błąd Następnie <customErrors mode="Off"/> lub <customErrors mode="RemoteOnly"/> użytkownicy widzą customErrors html, Then customErrors mode = " On " ten kod jest zły too


Kolejny problem tego kodu, który

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

Strona zwrotna z kodem 302 zamiast prawdziwego kodu błędu (402,403 itd.)

 -1
Author: Александр Шмыков,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-24 09:18:09