Application_Error tidak aktif saat customerrors = "On"

124

Saya memiliki kode dalam peristiwa global.asaxfile Application_Erroryang dijalankan ketika terjadi kesalahan dan mengirimkan detail kesalahan ke email saya sendiri.

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

Ini berfungsi dengan baik ketika saya menjalankannya di Visual Studio, namun ketika saya menerbitkan ke server langsung kami Application_Erroracara tidak menyala.

Setelah beberapa pengujian, saya bisa mendapatkan Application_Errorpengaktifan saat saya menyetelnya customErrors="Off", namun menyetelnya kembali untuk customErrors="On"menghentikan acara agar tidak diaktifkan lagi.

Adakah yang bisa menyarankan mengapa Application_Errortidak akan menembak ketika customErrorsdiaktifkan di web.config?

WDuffy
sumber
Saya mengalami masalah yang sama persis. Saya juga menemukan pertanyaan SO ini: stackoverflow.com/questions/3713939/… yang menyarankan untuk menempatkan server IIS7 ke mode klasik. Sayangnya itu bukan pilihan bagi kami. Ada yang punya solusi yang lebih baik?
Jesse Webb
Berikut adalah pertanyaan terkait lainnya dan jawabannya (tidak ada yang diterima) menyarankan untuk tidak menggunakan Application_Error () sama sekali ... stackoverflow.com/questions/1194578/…
Jesse Webb
@Gweebz Saya telah memposting jawaban tentang bagaimana saya mengatasi ini, tetapi saya masih tidak menemukan dokumentasi yang solid tentang mengapa saya mendapatkan perilaku ini.
WDuffy
Saya menambahkan jawaban yang menjelaskan mengapa Application_Error()metode tersebut tidak dipanggil. Saya juga menjelaskan solusi terakhir saya.
Jesse Webb
Sangat merekomendasikan artikel ini untuk mendapatkan kesalahan khusus bekerja.
ThomasArdal

Jawaban:

133

PEMBARUAN
Karena jawaban ini memang memberikan solusi, saya tidak akan mengeditnya, tetapi saya telah menemukan cara yang jauh lebih bersih untuk memecahkan masalah ini. Lihat jawaban saya yang lain untuk detailnya ...

Jawaban Asli:
Saya tahu mengapa Application_Error()metode ini tidak dipanggil ...

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

Secara default (saat proyek baru dibuat), aplikasi MVC memiliki beberapa logika di Global.asax.csfile. Logika ini digunakan untuk memetakan rute dan mendaftarkan filter. Secara default, ini hanya mendaftarkan satu filter: HandleErrorAttributefilter. Saat customErrors aktif (atau melalui permintaan jarak jauh saat disetel ke RemoteOnly), HandleErrorAttribute memberi tahu MVC untuk mencari tampilan Error dan tidak pernah memanggil Application_Error()metode tersebut. Saya tidak dapat menemukan dokumentasi ini tetapi dijelaskan dalam jawaban ini di programmers.stackexchange.com .

Untuk mendapatkan metode ApplicationError () yang dipanggil untuk setiap pengecualian yang tidak tertangani, cukup hapus baris yang mendaftarkan filter HandleErrorAttribute.

Sekarang masalahnya adalah: Bagaimana cara mengkonfigurasi customErrors untuk mendapatkan apa yang Anda inginkan ...

Secara default, bagian customErrors redirectMode="ResponseRedirect". Anda juga dapat menentukan atribut defaultRedirect sebagai rute MVC. Saya membuat ErrorController yang sangat sederhana dan mengubah web.config saya agar terlihat seperti ini ...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

Masalah dengan solusi ini adalah bahwa ia 302 mengalihkan ke URL kesalahan Anda dan kemudian laman tersebut merespons dengan kode status 200. Hal ini menyebabkan Google mengindeks halaman kesalahan yang buruk. Ini juga tidak terlalu sesuai dengan spesifikasi HTTP. Yang ingin saya lakukan adalah tidak mengalihkan, dan menimpa tanggapan asli dengan tampilan kesalahan khusus saya.

Saya mencoba untuk berubah redirectMode="ResponseRewrite". Sayangnya, opsi ini tidak mendukung rute MVC , hanya halaman HTML statis atau ASPX. Saya mencoba menggunakan halaman HTML statis pada awalnya tetapi kode respons masih 200 tetapi, setidaknya itu tidak mengarahkan. Saya kemudian mendapat ide dari jawaban ini ...

Saya memutuskan untuk menyerah pada MVC karena penanganan kesalahan. Saya membuat Error.aspxdan a PageNotFound.aspx. Halaman-halaman ini sangat sederhana tetapi memiliki satu keajaiban ...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

Blok ini memberi tahu halaman yang akan disajikan dengan kode status yang benar. Secara kasar, pada halaman PageNotFound.aspx, saya menggunakan HttpStatusCode.NotFoundsebagai gantinya. Saya mengubah web.config saya agar terlihat seperti ini ...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

Semuanya bekerja dengan sempurna!

Ringkasan:

  • Hapus garis: filters.Add(new HandleErrorAttribute());
  • Gunakan Application_Error()metode untuk mencatat pengecualian
  • Gunakan customErrors dengan ResponseRewrite, yang mengarah ke halaman ASPX
  • Buat halaman ASPX bertanggung jawab atas kode status responsnya sendiri

Ada beberapa kelemahan yang saya perhatikan dengan solusi ini.

  • Halaman ASPX tidak dapat membagikan markup apa pun dengan template Razor, saya harus menulis ulang markup header dan footer standar situs web kami untuk tampilan dan nuansa yang konsisten.
  • Halaman * .aspx dapat diakses secara langsung dengan menekan URL-nya

Ada solusi untuk masalah ini tetapi saya tidak cukup khawatir dengan mereka untuk melakukan pekerjaan tambahan.

Saya harap ini membantu semua orang!

Jesse Webb
sumber
2
+1! Solusi yang benar-benar bagus, tetapi apa konsekuensi dari mencampur MVC dengan halaman aspx?
Diego
2
Kami telah mengalami ini di PROD selama beberapa bulan sekarang dan saya belum menemukan dan dampak negatifnya. Satu-satunya 'gotcha' adalah kami harus mengubah CI kami dan menerapkan tugas AspCompile MSBUILD karena MVC tidak membutuhkannya tetapi ketika kami menambahkan file .as , mereka memerlukannya. Mungkin ada masalah lain tapi belum muncul ...
Jesse Webb
Saya telah mencoba menggunakan pendekatan ini di MVC4, dan tampaknya itu hanya berfungsi untuk saya ketika saya mendapatkannya <customErrors mode="Off" />. Telah filters.Add(new HandleErrorAttribute());dihapus atau tidak ada efeknya.
Grzegorz Sławecki
di halaman aspx Anda dapat menambahkan panggilan ajax untuk memanggil tampilan yang ingin Anda tampilkan. Menghemat harus menggandakan kode
Mike
71

Saya menyelesaikan ini dengan membuat ExceptionFilter dan mencatat kesalahan di sana alih-alih Application_Error. Yang perlu Anda lakukan hanyalah menambahkan panggilan ke di RegisterGlobalFilters

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}
Menandai
sumber
6
+1 Jika jawaban ini berhasil, menurut saya ini adalah cara yang paling elegan dan mungkin dimaksudkan (oleh kerangka kerja MVC) untuk menangani kesalahan.
Matt Hamsmith
2
Ini bekerja dengan sempurna untuk saya dan jauh lebih rapi daripada jawaban yang diterima di sini. Bagus!
Alex Warren
3
Ini akan bekerja untuk memecahkan masalah pengecualian logging. Bagaimana Anda menggabungkan ini dengan menampilkan halaman kesalahan kustom?
Jesse Webb
3
Saya mencoba ini dan berhasil dengan baik kecuali dalam kasus 404. Untuk 404, ini tidak akan menampilkan tampilan Errors.cshtml, itu hanya akan memberi saya YSoD. Jika 404 kustom tidak diperlukan, solusi ini pasti lebih bersih!
Jesse Webb
1
Saya suka itu. Bekerja dengan 'CustomErrors = On'
Illidan
36

Saya menemukan artikel yang menjelaskan cara yang jauh lebih bersih untuk membuat halaman kesalahan kustom di aplikasi web MVC3 yang tidak mencegah kemampuan untuk mencatat pengecualian.

Solusinya adalah dengan menggunakan <httpErrors> elemen <system.webServer>section.

Saya mengkonfigurasi file Web.config seperti ini ...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

Saya juga dikonfigurasi customErrorsuntuk memiliki mode="Off"(seperti yang disarankan oleh artikel).

Itu membuat respons diganti dengan tindakan ErrorController. Ini pengontrolnya:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

Tampilannya sangat lurus ke depan, saya hanya menggunakan sintaks Razor standar untuk membuat halaman.

Itu saja sudah cukup bagi Anda untuk menggunakan halaman kesalahan kustom dengan MVC.

Saya juga membutuhkan pencatatan Pengecualian jadi saya mencuri solusi Mark dengan menggunakan ExceptionFilter kustom ...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

Hal terakhir yang perlu Anda lakukan adalah mendaftarkan Filter Pengecualian di file Global.asax.cs Anda :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

Ini terasa seperti solusi yang jauh lebih bersih daripada jawaban saya sebelumnya dan bekerja sebaik yang saya tahu. Saya menyukainya terutama karena saya tidak merasa sedang berjuang melawan kerangka MVC; solusi ini benar-benar memanfaatkannya!

Jesse Webb
sumber
6
Perlu disebutkan bahwa menurut saya solusi ini hanya berfungsi di IIS 7 dan yang lebih baru; elemen httpErrors baru saja ditambahkan.
Jesse Webb
Jawaban ini tidak berhasil untuk saya, ada saya, itu memberi saya warna biru yang mengerikan: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. Layar kesalahan, bukan /Error/Indexhalaman yang saya minta
Serj Sagan
@SerjSagan Saya baru saja mencoba ini di proyek baru untuk menguji langkah-langkahnya dan berhasil dengan baik. Apakah Anda menggunakan IIS, IIS Express, atau VS Dev Server? Ini tidak akan berfungsi di VS Dev Server. Ketika saya mengatur proyek saya untuk menggunakan VS Dev Server, bukan IIS, saya melihat kesalahan layar kuning dan bukan halaman kesalahan kustom.
Jesse Webb
1
@SerjSagan Tapi sepertinya Anda mendapatkan kesalahan layar Biru IIS, bukan kesalahan layar Kuning klasik. Ini membuat saya menganggap Anda menggunakan beberapa bentuk IIS. Jika demikian, bacalah artikel yang saya tautkan di kalimat pertama, khususnya bagian terakhir yang berjudul: "Beberapa catatan penting". Dikatakan:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb
3
@SerjSagan Mengubah errorMode ke DetailedLocalOnly atau Custom akan menampilkan halaman Anda.
Daniel P
8

Dalam kasus ASP.NET MVC5 penggunaan

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

Anda dapat menemukannya di FilterConfig.csdari App_Startfolder.

Pengembang
sumber
1

Saya suka jawaban Mark dengan ExceptionFilter, tetapi opsi lain, jika Anda memiliki semua pengontrol yang berasal dari pengontrol dasar yang sama, adalah dengan mengganti OnException di pengontrol dasar Anda. Anda dapat melakukan logging dan email di sana. Ini memiliki keuntungan untuk dapat menggunakan dependensi apa pun yang sudah Anda masukkan ke pengontrol dasar Anda dengan wadah IoC Anda.

Anda masih dapat menggunakan IoC Anda dengan IExceptionFilter, tetapi sedikit lebih rumit untuk mengonfigurasi binding Anda.

Tim Hardy
sumber
0

Sejauh yang saya tahu, Anda memberikan kontrol ke Halaman yang ditentukan dalam parameter url dan pemberitahuan peristiwa Anda akan duduk di sini, bukan Application_Error

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

Banyak informasi dapat ditemukan di sini: http://support.microsoft.com/kb/306355

ChrisBint
sumber
Terima kasih Chris, saya membaca dokumentasinya dan itu menyarankan bahwa Application_Error akan dipanggil ketika terjadi kesalahan yang tidak tertangani (yang saya harapkan) dan itu seharusnya Server.ClearError () tidak dipanggil bagian web.config customErrors akan menjadi titik pegangan terakhir. Namun, mengaktifkan customErrors adalah hal yang menghentikan Application_Error dari pengaktifan.
WDuffy
Dokumentasi yang Anda tautkan berbicara tentang aplikasi ASP.NET dan tampaknya aplikasi web MVC3 berperilaku berbeda.
Jesse Webb
0

Untuk mengatasi ini, saya akhirnya membiarkan kesalahan pelanggan dinonaktifkan dan menangani semua kesalahan dari acara Application_Error di global.asax. Ini sedikit rumit dengan MVC karena saya tidak ingin mengembalikan 301 redirect, saya ingin mengembalikan kode kesalahan yang sesuai. Lebih jelasnya dapat dilihat di blog saya di http://www.wduffy.co.uk/blog/using-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/ tetapi kode akhirnya adalah tercantum di bawah ...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

Dan inilah pengontrolnya

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}
WDuffy
sumber
Saya mencoba solusi Anda tetapi tidak berhasil untuk saya. Dengan garis Response.StatusCode = ###;, itu menunjukkan halaman kesalahan MVC built-in di C:\inetpub\custerr\en-US. Saya juga tidak menyukai gagasan untuk memanggil HttpHandlers atau Controllers secara manual dari metode Application_Error () saya. Saya senang Anda menemukan solusi untuk masalah Anda, saya tahu sakit kepala seperti apa yang saya alami.
Jesse Webb
0

Entri blog ini membantu saya:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

Jika Anda menggunakan IIS 7.0 atau lebih tinggi, Anda dapat mengubah file Web.config Anda untuk menangani permintaan yang terlalu besar. Ada beberapa peringatan, tetapi berikut ini contohnya:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Ada detail tambahan tentang elemen file konfigurasi ini di sini:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

Kode status 404.13 didefinisikan sebagai "Panjang Isi Terlalu Besar". Satu hal penting untuk diperhatikan adalah bahwa maxAllowedContentLengthditentukan dalam byte. Ini berbeda dari maxRequestLengthpengaturan yang Anda temukan di <system.web>bagian, yang ditentukan dalam kilobyte.

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

Perhatikan juga bahwa pathatribut harus berupa jalur absolut jika responseModeada Redirect, jadi tambahkan nama direktori virtual, jika relevan. Jawaban informatif Jesse Webb menunjukkan bagaimana melakukan ini responseMode="ExecuteURL", dan menurut saya pendekatan itu akan berhasil juga.

Pendekatan ini tidak bekerja jika Anda mengembangkan menggunakan Visual Studio Development Server (Cassini, server Web yang terintegrasi ke dalam Visual Studio). Saya berasumsi ini akan berhasil di IIS Express, tetapi saya belum mengujinya.

Dan Jagnow
sumber
0

Saya mengalami masalah yang sama di mana Application_Error() tidak tertabrak. Saya mencoba segalanya, sampai akhirnya saya melangkah melalui apa yang terjadi. Saya memiliki beberapa kode khusus dalam acara ELMAH yang menambahkan JSON ke email yang dikirimnya, dan ada kesalahan nol di sana!

Memperbaiki kesalahan internal memungkinkan kode untuk melanjutkan ke Application_Error()acara seperti yang diharapkan.

Matt Kemp
sumber