ASP.NET MVC HandleError

110

Bagaimana cara saya tentang [HandleError]filter di asp.net MVC Preview 5?
Saya mengatur customErrors di file Web.config saya

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

dan letakkan [HandleError] di atas Kelas Pengontrol saya seperti ini:

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

Kemudian saya membiarkan pengontrol saya mewarisi dari kelas ini dan memanggil CrashTest () pada mereka. Visual studio berhenti karena kesalahan dan setelah menekan f5 untuk melanjutkan, saya dialihkan ke Error.aspx? Aspxerrorpath = / sxi.mvc / CrashTest (di mana sxi adalah nama pengontrol yang digunakan. Tentu saja jalur tidak dapat ditemukan dan saya mendapatkan "Server Error dalam Aplikasi '/'." 404.

Situs ini di-porting dari pratinjau 3 ke 5. Semuanya berjalan (tidak banyak bekerja untuk port) kecuali penanganan kesalahan. Ketika saya membuat proyek baru yang lengkap, penanganan kesalahan tampaknya berfungsi.

Ide ide?

--Catatan--
Karena pertanyaan ini memiliki lebih dari 3K tampilan sekarang, saya pikir akan bermanfaat untuk memasukkan apa yang saya gunakan saat ini (ASP.NET MVC 1.0). Dalam proyek kontribusi MVC ada atribut brilian yang disebut "RescueAttribute" Anda mungkin harus memeriksanya juga;)

Boris Callens
sumber
Tautan ke RescueAttributesumber: mvccontrib.codeplex.com/SourceControl/changeset/view/…
Peter

Jawaban:

158
[HandleError]

Ketika Anda hanya menyediakan atribut HandleError ke kelas Anda (atau metode tindakan Anda dalam hal ini), maka ketika terjadi pengecualian yang tidak tertangani MVC akan mencari Tampilan terkait bernama "Kesalahan" terlebih dahulu di folder Tampilan Pengontrol. Jika tidak dapat menemukannya di sana maka itu akan melanjutkan untuk mencari di folder Shared View (yang seharusnya memiliki file Error.aspx di dalamnya secara default)

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

Anda juga dapat menumpuk atribut tambahan dengan informasi spesifik tentang jenis pengecualian yang Anda cari. Pada saat itu, Anda dapat mengarahkan Error ke tampilan tertentu selain tampilan default "Error".

Untuk informasi lebih lanjut, lihat entri blog Scott Guthrie tentang itu.

Elijah Manor
sumber
1
Terima kasih atas informasi yang diperpanjang. Saya tidak tahu apa yang saya lakukan salah, tetapi saya membuat proyek baru, memindahkan semua tampilan, pengontrol, dan model yang ada di dalamnya dan sekarang berfungsi. Tidak tahu tentang pandangan selektif.
Boris Callens
Jika pencatatan pengecualian ini diinginkan, apakah ini tempat yang dapat diterima untuk menambahkan kode di belakang tampilan?
Peter J
6
Ikonik, inilah balasan "lebih baik terlambat daripada tidak sama sekali" untuk komentar Anda: Anda dapat membuat subkelas HandleErrorAttribute dan mengganti metode "OnException": Kemudian, masukkan logging atau tindakan kustom apa pun yang Anda inginkan. Anda kemudian dapat sepenuhnya menangani pengecualian (menyetel konteks.ExceptionHandled ke true), atau menunda kembali ke metode OnException kelas dasar untuk ini. Berikut adalah artikel luar biasa yang dapat membantu dalam hal ini: blog.dantup.me.uk/2009/04/…
Funka
Saya memiliki banyak pengontrol jadi dapatkah saya menangani ini di dalam global.asaxseperti ini untuk menampilkan pesan kepada pengguna?
shaijut
@Bagaimana dengan menggunakan halaman kesalahan yang sama dengan PartialView dan merendernya pada dialog modal setelah pengecualian terjadi? Bisakah Anda memberikan contoh dalam jawaban Anda? Apa yang ingin saya capai dijelaskan tentang penanganan Kesalahan Global menggunakan PartialView di MVC .
Jack
23

Perlu juga diperhatikan bahwa kesalahan yang tidak menyetel kode kesalahan http menjadi 500

(mis. UnauthorizedAccessException)

tidak akan ditangani oleh filter HandleError.

Corin Blaikie
sumber
1
Benar, tapi lihat RescueAttribute di MVC contrib (tautan di OP)
Boris Callens
14

Solusi untuk kode kesalahan http ke 500 ini adalah atribut yang disebut [ERROR] meletakkannya pada tindakan

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

}

//CONTOH:

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

Atribut dalam MVC sangat berguna dalam penanganan error pada metode get dan post , juga melacak panggilan ajax .

Buat pengontrol dasar dalam aplikasi Anda dan warisi di pengontrol utama (EmployeeController).

public class EmployeeController: BaseController

Tambahkan kode di bawah ini di pengontrol dasar.

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

}

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

Menemukan Direktori: Root / App_Start / FilterConfig.cs

Tambahkan kode di bawah ini:

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

Lacak Kesalahan AJAX:

Panggil fungsi CheckAJAXError dalam pemuatan halaman tata letak.

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 - Pengembang Full Stack
sumber
Anda membocorkan detail pengecualian pada permintaan AJAX, Anda tidak selalu menginginkannya. Kode logging Anda tidak aman untuk thread. Anda berasumsi ada tampilan Error dan mengembalikannya, tanpa mengubah kode respons. Kemudian Anda memeriksa string kesalahan tertentu di JavaScript (bagaimana dengan pelokalan?). Pada dasarnya Anda mengulangi apa yang sudah dikatakan oleh jawaban yang sudah ada: "Ganti OnExceptionuntuk menangani pengecualian" , tetapi menunjukkan implementasi yang sangat buruk.
CodeCaster
Apa itu parameter @ School.Resource.Messages.ReferanceExist?
Jack
@ CodeCaster Apakah Anda tahu cara yang lebih baik untuk menggunakan metode penanganan kesalahan semacam itu dengan AJAX di ASP.NET MVC? Tolong bantu?
Jack
Kembalikan 400 atau 500, seperti yang dimaksudkan HTTP. Jangan mencari string tertentu di badan respons.
CodeCaster
@CodeCaster Bisakah Anda melihat penanganan Kesalahan Global menggunakan PartialView di MVC terkait masalah ini?
Jack
4

Anda kehilangan Error.aspx :) Dalam pratinjau 5, ini terletak di folder Views / Shared. Cukup salin dari proyek Pratinjau 5 baru.

Ricky
sumber
Terima kasih atas balasannya, tapi saya sudah menyalin halaman Error.aspx. Mungkin memang sesuatu yang biasanya saya lupakan, tapi kali ini tidak. : 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();
    }

}

Mendongkrak
sumber