ASP.NET MVC HandleError


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. Все працює (не так вже й багато роботи), за винятком обробки помилок. Коли я створюю новий проект, обробка помилок, здається, працює.

Ідеї?

- Примітка.
Оскільки зараз у цьому питанні більше 3К переглядів, я подумав, що було б корисно вкласти те, що я зараз (ASP.NET MVC 1.0) використовую. У проекті mvc contrib є геніальний атрибут під назвою "RescueAttribute". Ви, ймовірно, також повинні це перевірити;)


Посилання на RescueAttributeджерело: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Пітер

Відповіді:


158
[HandleError]

Якщо ви надаєте лише атрибут HandleError для свого класу (або ваш метод дій з цього приводу), тоді, коли трапиться необроблений виняток, MVC спочатку шукатиме відповідний вид з назвою "Помилка" у папці View Controller's View. Якщо він не може його знайти там, тоді він перегляне папку «Спільний вигляд» (яка за замовчуванням повинна містити файл Error.aspx)

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

Ви також можете складати додаткові атрибути з конкретною інформацією про тип винятку, який шукаєте. У цей момент ви можете направити помилку на певний вигляд, відмінний від типового перегляду "Помилка".

Для отримання додаткової інформації дивіться у блозі Скотта Гетрі про це.


1
Дякуємо за розширену інформацію. Я не знаю, що я зробив не так, але я створив новий проект, переніс усі існуючі погляди, контролери та моделі в ньому і зараз він працює. Хоча не знав про вибіркові погляди.
Борис Калленс

Якщо бажано ввійти в журнал цих винятків, чи було б це прийнятним місцем для додавання коду позаду для подання?
Пітер Дж

6
Культурно, ось мій "краще пізно ніж ніколи" відповідь на ваш коментар: Ви можете замість цього підкласирувати HandleErrorAttribute та замінити його метод "OnException": Потім вставте будь-які журнали чи спеціальні дії, які ви хочете. Тоді ви можете або повністю обробити виняток (встановити контекст.ExceptionHandled to true), або відкласти назад до власного методу OnException базового класу для цього. Ось відмінна стаття , яка може допомогти з цим: blog.dantup.me.uk/2009/04 / ...
Funka

У мене є багато контролерів , так я можу впоратися з цим всередині , global.asaxяк це , щоб показати повідомлення для користувачів?
shaijut

@ Що щодо використання тієї ж сторінки помилок, що і PartialView, і відображення її в діалоговому вікні модалів після виникнення винятку? Чи можете ви надати приклад у своїй відповіді? Чого я хочу досягти, було пояснено щодо обробки глобальних помилок за допомогою PartialView в MVC .
Джек

23

Слід також зазначити, що помилки, які не встановлюють код помилки http, становлять 500

(наприклад, несанкціонованеAccessException)

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


1
Правда, але перевірте RescueAttribute у MVC contrib (посилання в ОП)
Борис Калленс

14

Рішення для коду помилки http до 500 - це атрибут під назвою [ПОМИЛКА], поставлений на дію

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

Ви просочуєте деталі винятків на запити AJAX, цього не завжди потрібно. Ваш код реєстрації не захищений від потоку. Ви припускаєте, що існує перегляд помилки та повертаєте її, не змінюючи код відповіді. Потім ви переходите до перевірки певних рядків помилок у 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();
    }

}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.