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 ทุกอย่างทำงาน (ไม่ได้ทำงานกับพอร์ตมากนัก) ยกเว้นการจัดการข้อผิดพลาด เมื่อฉันสร้างโครงการใหม่ที่สมบูรณ์ดูเหมือนว่าการจัดการข้อผิดพลาดจะใช้งานได้

ไอเดีย?

- หมายเหตุ -
เนื่องจากคำถามนี้มียอดดูมากกว่า 3K ตอนนี้ฉันคิดว่ามันจะเป็นประโยชน์ที่จะนำสิ่งที่ฉันกำลังใช้อยู่ (ASP.NET MVC 1.0) มาใช้ ในโปรเจ็กต์การสนับสนุน mvcมีแอตทริบิวต์ที่ยอดเยี่ยมชื่อว่า "RescueAttribute" คุณควรตรวจสอบด้วย;)


ลิงก์ไปยังRescueAttributeแหล่งที่มา: mvccontrib.codeplex.com/SourceControl/changeset/view/…
ปีเตอร์

คำตอบ:


158
[HandleError]

เมื่อคุณระบุเฉพาะแอตทริบิวต์ HandleError ให้กับชั้นเรียนของคุณ (หรือวิธีการดำเนินการของคุณสำหรับเรื่องนั้น) เมื่อเกิดข้อยกเว้นที่ไม่สามารถจัดการได้ MVC จะมองหามุมมองที่เกี่ยวข้องชื่อ "ข้อผิดพลาด" ก่อนในโฟลเดอร์ View ของคอนโทรลเลอร์ ถ้าหาไม่เจอก็จะเข้าไปดูในโฟลเดอร์ Shared View (ซึ่งควรมีไฟล์ Error.aspx อยู่ตามค่าเริ่มต้น)

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

คุณยังสามารถซ้อนแอตทริบิวต์เพิ่มเติมพร้อมข้อมูลเฉพาะเกี่ยวกับประเภทของข้อยกเว้นที่คุณกำลังมองหา เมื่อถึงจุดนั้นคุณสามารถกำหนดข้อผิดพลาดไปยังมุมมองเฉพาะที่นอกเหนือจากมุมมอง "ข้อผิดพลาด" เริ่มต้น

สำหรับข้อมูลเพิ่มเติมโปรดดูที่บล็อกโพสต์ของ Scott Guthrieเกี่ยวกับเรื่องนี้


1
ขอบคุณสำหรับข้อมูลเพิ่มเติม ฉันไม่รู้ว่าฉันทำอะไรผิด แต่ฉันสร้างโปรเจ็กต์ใหม่พอร์ตมุมมองคอนโทรลเลอร์และโมเดลที่มีอยู่ทั้งหมดและตอนนี้ก็ใช้งานได้แล้ว ไม่ทราบเกี่ยวกับมุมมองที่เลือก
Boris Callens

หากต้องการบันทึกข้อยกเว้นเหล่านี้จะเป็นสถานที่ที่ยอมรับได้ในการเพิ่มโค้ดหลังมุมมองหรือไม่
Peter J

6
Iconic นี่คือการตอบกลับความคิดเห็นของคุณ "ดีกว่าไม่มาสาย" ของฉัน: คุณสามารถซับคลาส HandleErrorAttribute แทนและแทนที่เมธอด "OnException" ของมันได้: จากนั้นแทรกการบันทึกหรือการดำเนินการแบบกำหนดเองที่คุณต้องการ จากนั้นคุณสามารถจัดการข้อยกเว้นได้อย่างสมบูรณ์ (บริบทการตั้งค่า ExceptionHandled เป็น true) หรือเลื่อนกลับไปใช้เมธอด OnException ของคลาสพื้นฐานสำหรับสิ่งนี้ นี่คือบทความดีๆ
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 เป็นประโยชน์อย่างมากในการจัดการข้อผิดพลาดที่ได้รับการโพสต์และวิธีการก็ยังติดตามสำหรับการโทรอาแจ็กซ์

สร้างตัวควบคุมพื้นฐานในแอปพลิเคชันของคุณและสืบทอดในตัวควบคุมหลักของคุณ (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
Boris Callens

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

}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.