18

У меня есть следующие в моем web.config:В ASP.NET MVC, что является лучшим показом необработанных исключений на мой взгляд?

<customErrors mode="On" defaultRedirect="Error"> 
    <error statusCode="404" redirect="Error/NotFound" /> 
</customErrors> 

У меня есть

[HandleError] 

в верхней части моего класса HomeController. Чтобы проверить, я создаю и действие, которое просто выдает исключение. , и перенаправляет на мой метод

ErrorController/Index 

но когда он попадает на мой взгляд, который связывается с HandleErrorInfo моя модель равна нулю, так что я каким-то образом потеряли ссылку на эту ошибку.

Уверен, что это как-то связано с ошибкой, потерянной в перенаправлении, поэтому я хотел посмотреть, не пропал ли я что-то, и если у кого-то есть предложения, где я могу получить представление, отображающее сообщение Stacktrace и сообщение об ошибке.

+0

customError теряет всю ссылку на ошибку. –

+0

Возможный дубликат [Как обрабатывать исключенные исключения в приложении ASP.NET MVC 3?] (Http://stackoverflow.com/questions/6596648/how-do-i-handle-uncaught-exceptions-in-an- asp-net-mvc-3-application) –

+0

В то время как customError перенаправляет, он создает новый HTTP-запрос, чтобы потерять все предыдущие данные ответа. Вам нужно установить redirectMode в ResponseRewrite, чтобы вы не сделали новый запрос. –

ответ

22

Я вижу неправильное представление. Вы хотите сделать вещь MVC и redirect для действия контроллера.

Но defaultRedirect сам по себе является соглашением Web Form и тем самым ограничен. Момент, когда вы перенаправлять на другой контроллер, то вы потеряете ваш HttpContext, и тем самым потерять свой HandleErrorInfo объект

Ваш [HandleError] Атрибут требует View, чтобы направить его сообщение об ошибке. Следуя приведенному выше примеру, я предполагаю, что у вас есть папка Views/Error для вашего ErrorController, и в ней вы видите Index View. Если вы хотите, чтобы ваш фильтр контекста, чтобы отправить HandleErrorInfo объект этой точки зрения,

Попробуйте этот синтаксис:

[HandleError(View="~/Views/Error/Index")] 
Public class HomeController : Controller 

Но что насчет Logging?!?!?

Я подозреваю, что ваши намерения более, чем просто отображение стека ошибок пользователей. На самом деле, я подозреваю, что у вас нет такого намерения. Я подозреваю, что ваша настоящая цель - зарегистрировать вашу ошибку (возможно, до db) и отобразить небольшое сообщение для вашего пользователя.

То, что я объяснил до сих пор, было «, что лучше [способ] показать необработанные исключения на мой взгляд». Для этого подходит атрибут [HandleError].

Но если вы хотите, чтобы перейти к следующему шагу (протоколирование ошибки) у вас есть несколько вариантов:

1) Override your base controller's On Exception method; создайте свой собственный Controller, наследующий класс MVC Controller, но переопределите метод исключения. Этот подход может использоваться в сочетании с атрибутом [HandleError]

2) Create a custom exception handler Создайте свой собственный обработчик исключений, который регистрирует ошибку. Затем ваш обработчик исключений может вызвать вид выбора или может работать совместно с [HandleError(order=2)], поскольку атрибуты фильтра могут принимать аргумент порядка, применяя приоритет.


Нитин Sawant спрашивает, что такое мнение об ошибке будет выглядеть следующим образом.

@model System.Web.Mvc.HandleErrorInfo 
<h2>Exception details</h2> 
<p> Controller: @Model.ControllerName </p> 
<p> Action: @Model.ActionName </p> 
<p> Exception: @Model.Exception </p> 
+0

Вы можете разместить содержание '~/Views/Error/Index'? –

+1

@NitinSawant, надеюсь, это поможет –

1

Я использовал этот маленький фрагмент кода, чтобы показать страницу с обработанными пользователями ошибками. Не была ли найдена страница или какая-либо другая ошибка.

void Application_Error(object sender, EventArgs e) 
    { 
     // this value can be fetched from config or depend on DEBUG smybol 
     if (!handleErrors) 
      return; 

     var error = Server.GetLastError(); 
     var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500; 

     if (code == 404) 
     { 
      // do something if page was not found. log for instance 
     } 
     else 
     { 
      // collect request info and log exception 
     } 

     // pass exception to ErrorsController 
     Request.RequestContext.RouteData.Values["ex"] = error; 

     // execute controller action 
     IController errorController = new ErrorsController(); 
     errorController.Execute(new RequestContext(new HttpContextWrapper(Context), Request.RequestContext.RouteData)); 
    } 

И контроллер ошибок выглядит примерно так. Если вам нужно подробное исключение, оно доступно через RouteData

public class ErrorsController : Controller 
{ 
    /// <summary> 
    /// Page not found 
    /// </summary> 
    /// <returns></returns> 
    public ActionResult Http404() 
    { 
     return View(); 
    } 

    /// <summary> 
    /// All other errors 
    /// </summary> 
    /// <param name="actionName"></param> 
    protected override void HandleUnknownAction(string actionName) 
    { 
     // in case detailed exception is required. 
     var ex = (Exception) RouteData.Values["ex"]; 
     return View(); 
    } 
} 

Вы можете добавить разные виды для каждого http-кода. Просто выполните действие Http {Code}

6

Я делаю что-то похожее на maxlego, которое обрабатывает все ошибки (а не только те, что встречаются в контроллерах с атрибутом HandleError).

Мой класс MvcApplication (в Global.asax.cs) имеет следующее:

public class MvcApplication : HttpApplication 
{ 
    // usual stuff here... 

    protected void Application_Error(object sender, EventArgs e) 
    { 
     Server.HandleError(((MvcApplication)sender).Context); 
    } 
} 

Приведенный выше код использует метод расширения моего MVC библиотеку полезных вещей. При этом мне не нужны атрибуты обработки ошибок, customErrors config или настраиваемые фильтры. Вместо этого метод расширения будет регистрировать сведения об ошибке затем вызвать соответствующее представление, либо:

  • AccessDenied
  • NotFound
  • InternalServerError

метод расширения кода, чтобы сделать эту работу:

public static class HttpServerUtilityExtensions 
{ 
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); 

    public static void HandleError(this HttpServerUtility server, HttpContext httpContext) 
    { 
     var currentController = " "; 
     var currentAction = " "; 
     var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)); 

     if (currentRouteData != null) 
     { 
      if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString())) 
       currentController = currentRouteData.Values["controller"].ToString(); 

      if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString())) 
       currentAction = currentRouteData.Values["action"].ToString(); 
     } 

     var exception = server.GetLastError(); 
     Logger.ErrorException(exception.Message, exception); 

     var controller = DependencyResolver.Current.GetService<ErrorController>(); 
     var routeData = new RouteData(); 
     var action = "InternalServerError"; 

     if (exception is HttpException) 
     { 
      var httpEx = exception as HttpException; 

      switch (httpEx.GetHttpCode()) 
      { 
       case 404: 
        action = "NotFound"; 
        break; 

       case 401: 
        action = "AccessDenied"; 
        break; 
      } 
     } 

     httpContext.ClearError(); 
     httpContext.Response.Clear(); 
     httpContext.Response.StatusCode = exception is HttpException ? ((HttpException)exception).GetHttpCode() : 500; 
     httpContext.Response.TrySkipIisCustomErrors = true; 

     routeData.Values["controller"] = "Error"; 
     routeData.Values["action"] = action; 

     controller.ViewData.Model = new HandleErrorInfo(exception, currentController, currentAction); 
     ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData)); 
    } 
} 

Примечание. В приведенном выше примере используется NLog для регистрации данных об ошибках, но может быть легко меняют, чтобы поддержать что-то еще. Кроме того, этот метод учитывает ваш контейнер IoC при разрешении ErrorController.