ASP.NET MVC HandleError


110

Làm cách nào để sử dụng [HandleError]bộ lọc trong asp.net MVC Preview 5?
Tôi đặt customErrors trong tệp Web.config của mình

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

và đặt [HandleError] phía trên Lớp điều khiển của tôi như sau:

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

Sau đó, tôi để bộ điều khiển của mình kế thừa từ lớp này và gọi CrashTest () trên chúng. Visual studio tạm dừng do lỗi và sau khi nhấn f5 để tiếp tục, tôi được chuyển đến Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (trong đó sxi là tên của bộ điều khiển đã sử dụng. Tất nhiên là không thể tìm thấy đường dẫn và tôi nhận được "Lỗi Máy chủ trong Ứng dụng '/'." 404.

Trang web này đã được chuyển từ bản xem trước 3 sang bản 5. Mọi thứ đều chạy (không có nhiều công việc để chuyển) ngoại trừ việc xử lý lỗi. Khi tôi tạo một dự án mới hoàn chỉnh, việc xử lý lỗi dường như hoạt động.

Ý tưởng?

--Lưu ý--
Vì câu hỏi này hiện đã có hơn 3 nghìn lượt xem, nên tôi nghĩ sẽ có lợi nếu đưa những gì tôi hiện đang sử dụng (ASP.NET MVC 1.0) vào. Trong dự án đóng góp mvc, có một thuộc tính tuyệt vời được gọi là "RescueAttribute" Bạn có thể cũng nên kiểm tra nó;)


Liên kết tới RescueAttributenguồn: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Peter

Câu trả lời:


158
[HandleError]

Khi bạn chỉ cung cấp thuộc tính HandleError cho lớp của mình (hoặc phương thức hành động của bạn cho vấn đề đó), thì khi một ngoại lệ không được xử lý xảy ra, MVC sẽ tìm kiếm một Dạng xem tương ứng có tên "Lỗi" trước tiên trong thư mục Dạng xem của Bộ điều khiển. Nếu nó không thể tìm thấy nó ở đó thì nó sẽ tiến hành tìm kiếm trong thư mục Shared View (theo mặc định sẽ có tệp Error.aspx trong đó)

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

Bạn cũng có thể xếp chồng các thuộc tính bổ sung với thông tin cụ thể về loại ngoại lệ mà bạn đang tìm kiếm. Tại thời điểm đó, bạn có thể hướng Lỗi đến một dạng xem cụ thể khác với dạng xem "Lỗi" mặc định.

Để biết thêm thông tin, hãy xem bài đăng trên blog của Scott Guthrie về nó.


1
Cảm ơn về thông tin mở rộng. Tôi không biết mình đã làm gì sai, nhưng tôi đã tạo một dự án mới, chuyển tất cả các khung nhìn, bộ điều khiển và mô hình hiện có vào đó và bây giờ nó hoạt động. Tôi không biết về các quan điểm chọn lọc.
Boris Callens 10/08/08

Nếu bạn muốn ghi nhật ký các ngoại lệ này, thì đây có phải là nơi có thể chấp nhận được để thêm đoạn mã vào chế độ xem không?
Peter J

6
Mang tính biểu tượng, đây là câu trả lời "muộn còn hơn không" của tôi cho nhận xét của bạn: Thay vào đó, bạn có thể phân lớp HandleErrorAttribute và ghi đè phương thức "OnException" của nó: Sau đó, chèn bất kỳ thao tác ghi nhật ký hoặc tùy chỉnh nào mà bạn muốn. Sau đó, bạn có thể hoàn toàn xử lý ngoại lệ (đặt context.ExceptionHandled thành true) hoặc trì hoãn trở lại phương thức OnException của riêng lớp cơ sở cho việc này. Dưới đây là một bài báo tuyệt vời có thể giúp với điều này: blog.dantup.me.uk/2009/04/...
Funka

Tôi có rất nhiều bộ điều khiển vậy tôi có thể xử lý bên trong global.asaxnhư thế này để hiển thị thông báo cho người dùng không?
shaijut

@Vậy còn việc sử dụng cùng một trang lỗi với PartialView và hiển thị nó trên hộp thoại phương thức sau khi ngoại lệ xảy ra thì sao? Bạn có thể vui lòng cung cấp một ví dụ trong câu trả lời của bạn? Những gì tôi muốn đạt được đã được giải thích về cách xử lý Lỗi toàn cầu bằng cách sử dụng PartialView trong MVC .
Jack

23

Cũng cần lưu ý rằng lỗi không đặt mã lỗi http thành 500

(ví dụ: UnauthorizedAccessException)

sẽ không được xử lý bởi bộ lọc HandleError.


1
Đúng vậy, nhưng hãy kiểm tra các RescueAttribute trong MVC contrib (liên kết trong OP)
Boris Callens

14

Giải pháp cho mã lỗi http thành 500, đây là thuộc tính có tên [ERROR] đặt nó vào một hành động

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

}

//THÍ DỤ:

[Error]
[HandleError]
[PopulateSiteMap(SiteMapName="Mifel1", ViewDataKey="Mifel1")]
public class ApplicationController : Controller
{
}

12

Các thuộc tính trong MVC rất hữu ích trong việc xử lý lỗi tại phương thức get và post , nó cũng theo dõi cuộc gọi ajax .

Tạo một bộ điều khiển cơ sở trong ứng dụng của bạn và kế thừa nó trong bộ điều khiển chính của bạn (EmployeeController).

lớp công khai EmployeeController: BaseController

Thêm mã bên dưới trong bộ điều khiển cơ sở.

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

}

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

Tìm thư mục: Root / App_Start / FilterConfig.cs

Thêm mã dưới đây:

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

Theo dõi lỗi AJAX:

Gọi hàm CheckAJAXError khi tải trang bố cục.

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

Bạn đang làm rò rỉ chi tiết ngoại lệ về các yêu cầu AJAX, bạn không phải lúc nào cũng muốn điều đó. Mã ghi nhật ký của bạn không an toàn cho chuỗi. Bạn đang giả định rằng có chế độ xem Lỗi và trả lại mà không làm thay đổi mã phản hồi. Sau đó, bạn kiểm tra các chuỗi lỗi nhất định trong JavaScript (còn bản địa hóa thì sao?). Về cơ bản, bạn đang lặp lại câu trả lời hiện tại đã nói: "Ghi đè OnExceptionđể xử lý các trường hợp ngoại lệ" , nhưng cho thấy việc triển khai nó khá tệ.
CodeCaster

Tham số @ School.Resource.Messages.ReferanceExist là gì?
Jack

@CodeCaster Bạn có biết cách nào tốt hơn để sử dụng loại phương pháp xử lý lỗi như vậy với AJAX trong ASP.NET MVC không? Bất kỳ giúp đỡ xin vui lòng?
Jack

Trả về 400 hoặc 500, như HTTP dự định. Đừng đi tìm các chuỗi cụ thể trong phần nội dung phản hồi.
CodeCaster

@CodeCaster Bạn có thể vui lòng xem cách xử lý Lỗi toàn cầu khi sử dụng PartialView trong MVC liên quan đến vấn đề này không?
Jack

4

Bạn đang thiếu Error.aspx :) Trong bản xem trước 5, điều này nằm trong thư mục Lượt xem / Chia sẻ của bạn. Chỉ cần sao chép nó từ một dự án Preview 5 mới.


Cảm ơn bạn đã trả lời, nhưng tôi đã sao chép trang Error.aspx. Quả thực có thể là điều mà tôi thường quên, nhưng không phải lần này. : P
Boris Callens 9/10/08

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

}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.