Ошибка HandleError ASP.NET MVC

110

Как мне использовать [HandleError]фильтр в asp.net MVC Preview 5?
Я установил customErrors в своем файле Web.config

<customErrors mode="On" defaultRedirect="Error.aspx">
  <error statusCode="403" redirect="NoAccess.htm"/>
  <error statusCode="404" redirect="FileNotFound.htm"/>
</customErrors>

и поместите [HandleError] над моим классом контроллера следующим образом:

[HandleError]
public class DSWebsiteController: Controller
{
    [snip]
    public ActionResult CrashTest()
    {
        throw new Exception("Oh Noes!");
    }
}

Затем я позволяю своим контроллерам наследовать от этого класса и вызываю для них CrashTest (). Visual studio останавливается из-за ошибки, и после нажатия клавиши f5 для продолжения я перенаправляюсь на Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (где sxi - это имя используемого контроллера. Конечно, путь не может быть найден, и я получаю «Ошибка сервера в приложении '/'.» 404.

Этот сайт был перенесен с предварительного просмотра 3 на 5. Все работает (не так уж много работы для переноса), кроме обработки ошибок. Когда я создаю полностью новый проект, обработка ошибок, похоже, работает.

Идеи?

- Примечание.
Поскольку сейчас у этого вопроса более 3000 просмотров, я подумал, что было бы полезно добавить то, что я использую сейчас (ASP.NET MVC 1.0). В проекте mvc contrib есть замечательный атрибут под названием "RescueAttribute", вам, наверное, тоже стоит его проверить;)

Борис Калленс
источник
Ссылка на RescueAttributeисточник: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Питер

Ответы:

158
[HandleError]

Когда вы предоставляете своему классу (или методу действия) только атрибут HandleError, тогда при возникновении необработанного исключения MVC сначала будет искать соответствующее представление с именем «Ошибка» в папке представления контроллера. Если он не может найти его там, он продолжит поиск в папке общего просмотра (в которой по умолчанию должен быть файл Error.aspx).

[HandleError(ExceptionType = typeof(SqlException), View = "DatabaseError")]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "LameErrorHandling")]

Вы также можете складывать дополнительные атрибуты с конкретной информацией о типе исключения, которое вы ищете. На этом этапе вы можете направить ошибку в конкретное представление, отличное от представления по умолчанию «Ошибка».

Для получения дополнительной информации прочтите об этом сообщение в блоге Скотта Гатри .

Elijah Manor
источник
1
Спасибо за расширенную информацию. Я не знаю, что я сделал не так, но я создал новый проект, перенес в него все существующие представления, контроллеры и модели, и теперь он работает. Но про выборочные взгляды не знал.
Борис Калленс,
Если требуется ведение журнала этих исключений, будет ли это приемлемым местом для добавления выделенного кода в представление?
Питер Дж,
6
Iconic, вот мой ответ «лучше поздно, чем никогда» на ваш комментарий: вместо этого вы можете создать подкласс HandleErrorAttribute и переопределить его метод «OnException»: затем вставьте любое желаемое ведение журнала или настраиваемые действия. Затем вы можете либо полностью обработать исключение (установив для context.ExceptionHandled значение true), либо вернуться для этого к собственному методу OnException базового класса. Вот отличная статья, которая может в этом помочь: blog.dantup.me.uk/2009/04/…
Funka
У меня есть много контроллеров , так я могу справиться с этим внутри , global.asaxкак это , чтобы показать сообщение для пользователей?
Shaijut
@ Как насчет того, чтобы использовать ту же страницу ошибок, что и PartialView, и отображать ее в модальном диалоговом окне после возникновения исключения? Не могли бы вы привести пример в своем ответе? То, чего я хочу достичь, было объяснено при обработке глобальных ошибок с использованием PartialView в MVC .
Джек
23

Также следует отметить, что ошибки, которые не устанавливают код ошибки http на 500

(например, UnauthorizedAccessException)

не будет обрабатываться фильтром HandleError.

Корин Блейки
источник
1
Верно, но посмотрите RescueAttribute в MVC contrib (ссылка в OP)
Boris Callens
14

Решение для кода ошибки http на 500 это атрибут под названием [ERROR], поместите его в действие

public class Error: System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {

            if (filterContext.HttpContext.IsCustomErrorEnabled)
            {
                filterContext.ExceptionHandled = true;

            }
            base.OnException(filterContext);
            //OVERRIDE THE 500 ERROR  
           filterContext.HttpContext.Response.StatusCode = 200;
    }

    private static void RaiseErrorSignal(Exception e)
    {
        var context = HttpContext.Current;
      // using.Elmah.ErrorSignal.FromContext(context).Raise(e, context);
    } 

}

//ПРИМЕР:

[Error]
[HandleError]
[PopulateSiteMap(SiteMapName="Mifel1", ViewDataKey="Mifel1")]
public class ApplicationController : Controller
{
}
Рауль
источник
12

Атрибуты в MVC очень полезны при обработке ошибок в методах get и post , они также отслеживают вызов ajax .

Создайте базовый контроллер в своем приложении и унаследуйте его в основном контроллере (EmployeeController).

открытый класс EmployeeController: BaseController

Добавьте ниже код в базовый контроллер.

/// <summary>
/// Base Controller
/// </summary>
public class BaseController : Controller
{       
    protected override void OnException(ExceptionContext filterContext)
    {
        Exception ex = filterContext.Exception;

        //Save error log in file
        if (ConfigurationManager.AppSettings["SaveErrorLog"].ToString().Trim().ToUpper() == "TRUE")
        {
            SaveErrorLog(ex, filterContext);
        }

        // if the request is AJAX return JSON else view.
        if (IsAjax(filterContext))
        {
            //Because its a exception raised after ajax invocation
            //Lets return Json
            filterContext.Result = new JsonResult()
            {
                Data = Convert.ToString(filterContext.Exception),
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();

            filterContext.Result = new ViewResult()
            {
                //Error page to load
                ViewName = "Error",
                ViewData = new ViewDataDictionary()
            };

            base.OnException(filterContext);
        }
    }

    /// <summary>
    /// Determines whether the specified filter context is ajax.
    /// </summary>
    /// <param name="filterContext">The filter context.</param>
    private bool IsAjax(ExceptionContext filterContext)
    {
        return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
    }

    /// <summary>
    /// Saves the error log.
    /// </summary>
    /// <param name="ex">The ex.</param>
    /// <param name="filterContext">The filter context.</param>
    void SaveErrorLog(Exception ex, ExceptionContext filterContext)
    {
        string logMessage = ex.ToString();

        string logDirectory = Server.MapPath(Url.Content("~/ErrorLog/"));

        DateTime currentDateTime = DateTime.Now;
        string currentDateTimeString = currentDateTime.ToString();
        CheckCreateLogDirectory(logDirectory);
        string logLine = BuildLogLine(currentDateTime, logMessage, filterContext);
        logDirectory = (logDirectory + "\\Log_" + LogFileName(DateTime.Now) + ".txt");

        StreamWriter streamWriter = null;
        try
        {
            streamWriter = new StreamWriter(logDirectory, true);
            streamWriter.WriteLine(logLine);
        }
        catch
        {
        }
        finally
        {
            if (streamWriter != null)
            {
                streamWriter.Close();
            }
        }
    }

    /// <summary>
    /// Checks the create log directory.
    /// </summary>
    /// <param name="logPath">The log path.</param>
    bool CheckCreateLogDirectory(string logPath)
    {
        bool loggingDirectoryExists = false;
        DirectoryInfo directoryInfo = new DirectoryInfo(logPath);
        if (directoryInfo.Exists)
        {
            loggingDirectoryExists = true;
        }
        else
        {
            try
            {
                Directory.CreateDirectory(logPath);
                loggingDirectoryExists = true;
            }
            catch
            {
            }
        }

        return loggingDirectoryExists;
    }

    /// <summary>
    /// Builds the log line.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    /// <param name="logMessage">The log message.</param>
    /// <param name="filterContext">The filter context.</param>       
    string BuildLogLine(DateTime currentDateTime, string logMessage, ExceptionContext filterContext)
    {
        string controllerName = filterContext.RouteData.Values["Controller"].ToString();
        string actionName = filterContext.RouteData.Values["Action"].ToString();

        RouteValueDictionary paramList = ((System.Web.Routing.Route)(filterContext.RouteData.Route)).Defaults;
        if (paramList != null)
        {
            paramList.Remove("Controller");
            paramList.Remove("Action");
        }

        StringBuilder loglineStringBuilder = new StringBuilder();

        loglineStringBuilder.Append("Log Time : ");
        loglineStringBuilder.Append(LogFileEntryDateTime(currentDateTime));
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("Username : ");
        loglineStringBuilder.Append(Session["LogedInUserName"]);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ControllerName : ");
        loglineStringBuilder.Append(controllerName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("ActionName : ");
        loglineStringBuilder.Append(actionName);
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append("----------------------------------------------------------------------------------------------------------");
        loglineStringBuilder.Append(System.Environment.NewLine);

        loglineStringBuilder.Append(logMessage);
        loglineStringBuilder.Append(System.Environment.NewLine);
        loglineStringBuilder.Append("==========================================================================================================");

        return loglineStringBuilder.ToString();
    }

    /// <summary>
    /// Logs the file entry date time.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileEntryDateTime(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd-MMM-yyyy HH:mm:ss");
    }

    /// <summary>
    /// Logs the name of the file.
    /// </summary>
    /// <param name="currentDateTime">The current date time.</param>
    string LogFileName(DateTime currentDateTime)
    {
        return currentDateTime.ToString("dd_MMM_yyyy");
    }

}

================================================

Находит каталог: Root / App_Start / FilterConfig.cs

Добавьте ниже код:

/// <summary>
/// Filter Config
/// </summary>
public class FilterConfig
{
    /// <summary>
    /// Registers the global filters.
    /// </summary>
    /// <param name="filters">The filters.</param>
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

Отследить ошибку AJAX:

Вызовите функцию CheckAJAXError при загрузке страницы макета.

function CheckAJAXError() {
    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {

        var ex;
        if (String(thrownError).toUpperCase() == "LOGIN") {
            var url = '@Url.Action("Login", "Login")';
            window.location = url;
        }
        else if (String(jqXHR.responseText).toUpperCase().indexOf("THE DELETE STATEMENT CONFLICTED WITH THE REFERENCE CONSTRAINT") >= 0) {

            toastr.error('ReferanceExistMessage');
        }
        else if (String(thrownError).toUpperCase() == "INTERNAL SERVER ERROR") {
            ex = ajaxSettings.url;
            //var url = '@Url.Action("ErrorLog", "Home")?exurl=' + ex;
            var url = '@Url.Action("ErrorLog", "Home")';
            window.location = url;
        }
    });
};
Sandip - Разработчик полного стека
источник
Вы утекаете сведения об исключении в запросах AJAX, вам это не всегда нужно. Ваш код ведения журнала не является потокобезопасным. Вы предполагаете, что есть представление Error, и возвращаете его, не изменяя код ответа. Затем вы проверяете некоторые строки ошибок в JavaScript (как насчет локализации?). В основном вы повторяете то, что уже сказано в существующем ответе: «Переопределить OnExceptionдля обработки исключений» , но демонстрируете довольно плохую его реализацию.
CodeCaster
Что такое параметр @ School.Resource.Messages.ReferanceExist?
Джек
@CodeCaster Знаете ли вы лучший способ использования такого метода обработки ошибок с помощью AJAX в ASP.NET MVC? Любая помощь, пожалуйста?
Джек
Верните 400 или 500, как предполагается в HTTP. Не копайтесь в поисках конкретных строк в теле ответа.
CodeCaster
@CodeCaster Не могли бы вы взглянуть на обработку глобальных ошибок с использованием PartialView в MVC относительно этой проблемы?
Джек
4

Вам не хватает Error.aspx :) В предварительном просмотре 5 он находится в папке Views / Shared. Просто скопируйте его из нового проекта Preview 5.

Рики
источник
Спасибо за ответ, но я уже скопировал страницу Error.aspx. Я действительно мог бы забыть о чем-то, но не в этот раз. : P
Борис Калленс
-1
    [HandleError]
    public class ErrorController : Controller
    {        
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult NotAuthorized()
        {
            //401
            Response.StatusCode = (int)HttpStatusCode.Unauthorized;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult Forbidden()
    {
        //403
        Response.StatusCode = (int)HttpStatusCode.Forbidden;

        return View();
    }

    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult NotFound()
    {
        //404
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ViewResult ServerError()
    {
        //500
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

}

разъем
источник