menangkap semua pengecualian yang tidak tertangani di ASP.NET Web Api

113

Bagaimana cara menangkap semua pengecualian tidak tertangani yang terjadi di ASP.NET Web Api sehingga saya bisa mencatatnya?

Sejauh ini saya sudah mencoba:

  • Buat dan daftarkan ExceptionHandlingAttribute
  • Terapkan Application_Errormetode diGlobal.asax.cs
  • Berlangganan AppDomain.CurrentDomain.UnhandledException
  • Berlangganan TaskScheduler.UnobservedTaskException

The ExceptionHandlingAttributeberhasil menangani pengecualian yang dilemparkan dalam metode aksi controller dan filter tindakan, tetapi pengecualian lainnya tidak ditangani, misalnya:

  • Pengecualian dilempar ketika IQueryabledikembalikan oleh metode tindakan gagal dijalankan
  • Pengecualian dilemparkan oleh penangan pesan (yaitu HttpConfiguration.MessageHandlers)
  • Pengecualian diberikan saat membuat instance pengontrol

Pada dasarnya, jika pengecualian akan menyebabkan 500 Internal Server Error dikembalikan ke klien, saya ingin itu masuk. Menerapkan Application_Errormelakukan pekerjaan ini dengan baik di Formulir Web dan MVC - apa yang dapat saya gunakan di Api Web?

Joe Daley
sumber
Sudahkah Anda mencoba menggunakan Pemantauan Kesehatan ASP.NET ? Aktifkan saja dan lihat apakah pengecualian Anda tidak dicatat ke log peristiwa.
John Saunders
Pemantauan Kesehatan menangkap pengecualian pipeline MVC saya, tetapi bukan pengecualian pipeline Web Api saya.
Joe Daley
Terima kasih - Butuh beberapa saat bagi saya untuk mencari tahu mengapa saya tidak dapat mencatat masalah injeksi konstruktor / ketergantungan saya, di mana saya pikir saya telah mengurutkan logging WebAPI ...
Overflew

Jawaban:

156

Ini sekarang dimungkinkan dengan WebAPI 2.1 (lihat Yang Baru ):

Buat satu atau beberapa implementasi IExceptionLogger. Sebagai contoh:

public class TraceExceptionLogger : ExceptionLogger
{
    public override void Log(ExceptionLoggerContext context)
    {
        Trace.TraceError(context.ExceptionContext.Exception.ToString());
    }
}

Kemudian daftarkan dengan HttpConfiguration aplikasi Anda, di dalam config callback seperti ini:

config.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());

atau secara langsung:

GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
memutuskan
sumber
7
@NeilBarnwell Ya, Web API 2.1 sesuai dengan versi perakitan System.Web.Http 5.1.0 . Jadi, Anda memerlukan versi ini atau yang lebih baru untuk menggunakan solusi yang dijelaskan di sini. Lihat versi paket nuget
decates
2
500 kesalahan tertentu masih tidak tertangkap oleh ini, misalnya. HttpException - host jarak jauh menutup koneksi. Apakah masih ada tempat bagi global.asax Application_Error untuk menangani kesalahan di luar pemrosesan api web?
Avner
11
Saya suka betapa mendetailnya dokumen resmi di msdn dan yang sebenarnya diinginkan 99% pengembang hanyalah 8 baris kode untuk mencatat kesalahan.
Rocklan
20

Jawaban Yuval adalah untuk menyesuaikan tanggapan untuk pengecualian yang tidak tertangani yang ditangkap oleh API Web, bukan untuk pencatatan, seperti yang dicatat di halaman tertaut . Lihat bagian Kapan Menggunakan di halaman untuk detailnya. Logger selalu dipanggil tapi handler dipanggil hanya jika respon bisa dikirim. Singkatnya, gunakan pencatat untuk mencatat dan penangan untuk menyesuaikan respons.

By the way, saya menggunakan v5.2.3 assembly dan ExceptionHandlerkelas tidak memiliki HandleCoremetode. Setara, menurut saya, adalah Handle. Namun, subclass saja ExceptionHandler(seperti dalam jawaban Yuval) tidak akan berhasil. Dalam kasus saya, saya harus menerapkan IExceptionHandlersebagai berikut.

internal class OopsExceptionHandler : IExceptionHandler
{
    private readonly IExceptionHandler _innerHandler;

    public OopsExceptionHandler (IExceptionHandler innerHandler)
    {
        if (innerHandler == null)
            throw new ArgumentNullException(nameof(innerHandler));

        _innerHandler = innerHandler;
    }

    public IExceptionHandler InnerHandler
    {
        get { return _innerHandler; }
    }

    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    {
        Handle(context);

        return Task.FromResult<object>(null);
    }

    public void Handle(ExceptionHandlerContext context)
    {
        // Create your own custom result here...
        // In dev, you might want to null out the result
        // to display the YSOD.
        // context.Result = null;
        context.Result = new InternalServerErrorResult(context.Request);
    }
}

Perhatikan bahwa, tidak seperti logger, Anda mendaftarkan penangan Anda dengan mengganti penangan default, bukan menambahkan.

config.Services.Replace(typeof(IExceptionHandler),
    new OopsExceptionHandler(config.Services.GetExceptionHandler()));
Duoc Tran
sumber
1
Ini adalah solusi yang bagus, ini harus menjadi solusi yang diterima untuk 'menangkap atau mencatat semua kesalahan'. Saya tidak akan pernah tahu mengapa itu tidak berhasil untuk saya ketika saya baru saja memperluas ExceptionHandler.
Rajiv
Solusi yang bagus. Setelah pipeline MVC dimuat untuk permintaan, ini berfungsi dengan baik. IIS masih menangani pengecualian hingga saat itu, termasuk saat menjalankan OWIN di startup.cs. Namun, pada beberapa titik setelah spin up selesai memproses startup.cs, itu berfungsi dengan sangat baik.
Gustyn
18

Untuk menjawab pertanyaan saya sendiri, ini tidak mungkin!

Menangani semua pengecualian yang menyebabkan kesalahan server internal tampaknya seperti kemampuan dasar yang harus dimiliki API Web, jadi saya telah mengajukan permintaan dengan Microsoft untuk penanganan kesalahan global untuk API Web :

https://aspnetwebstack.codeplex.com/workitem/1001

Jika Anda setuju, buka tautan itu dan pilih!

Sementara itu, artikel bagus Penanganan Pengecualian API Web ASP.NET menunjukkan beberapa cara berbeda untuk menangkap beberapa kategori kesalahan yang berbeda. Ini lebih rumit dari yang seharusnya, dan tidak menangkap semua kesalahan server antar, tetapi ini adalah pendekatan terbaik yang tersedia saat ini.

Pembaruan: Penanganan kesalahan global sekarang diterapkan dan tersedia dalam versi malam! Ini akan dirilis di ASP.NET MVC v5.1. Begini cara kerjanya: https://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling

Joe Daley
sumber
Sepertinya alasan untuk menggunakan pengontrol untuk panggilan ajax daripada api web .. batas-batasnya sudah kabur .. meskipun jika ELMAH bisa menangkapnya, mungkin ada cara
Sonic Soul
4
Penanganan kesalahan global sekarang telah ditambahkan di Web API 2.1. Lihat jawaban saya untuk lebih jelasnya.
Decates
10

Anda juga dapat membuat penangan pengecualian global dengan mengimplementasikan IExceptionHandlerantarmuka (atau mewarisi ExceptionHandlerkelas dasar). Ini akan menjadi yang terakhir dipanggil dalam rantai eksekusi, setelah semua terdaftar IExceptionLogger:

IExceptionHandler menangani semua pengecualian yang tidak tertangani dari semua pengontrol. Ini yang terakhir dalam daftar. Jika pengecualian terjadi, IExceptionLogger akan dipanggil terlebih dahulu, kemudian controller ExceptionFilters dan jika masih tidak tertangani, implementasi IExceptionHandler.

public class OopsExceptionHandler : ExceptionHandler
{
    public override void HandleCore(ExceptionHandlerContext context)
    {
        context.Result = new TextPlainErrorResult
        {
            Request = context.ExceptionContext.Request,
            Content = "Oops! Sorry! Something went wrong."        
        };
    }

    private class TextPlainErrorResult : IHttpActionResult
    {
        public HttpRequestMessage Request { get; set; }

        public string Content { get; set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            HttpResponseMessage response = 
                             new HttpResponseMessage(HttpStatusCode.InternalServerError);
            response.Content = new StringContent(Content);
            response.RequestMessage = Request;
            return Task.FromResult(response);
        }
    }
}

Lebih lanjut tentang itu di sini .

Yuval Itzchakov
sumber
Hanya penasaran, di mana registerd ini?
Kamis
-1

Anda mungkin memiliki blok coba-tangkap yang tidak Anda sadari.

Saya pikir global.asax.Application_Errormetode baru saya tidak secara konsisten dipanggil untuk pengecualian yang tidak tertangani dalam kode lama kami.

Kemudian saya menemukan beberapa blok coba-tangkap di tengah tumpukan panggilan yang disebut teks Respon.Write on the Exception. Itu dia. Menghapus teks di layar lalu membunuh batu pengecualian itu.

Jadi pengecualian sudah ditangani, tetapi penanganannya tidak berguna. Setelah saya menghapus blok coba-tangkap itu, pengecualian disebarkan ke metode Application_Error seperti yang diharapkan.

Sumber
sumber