Menangani Pengecualian ASP.NET Menggunakan IRO.Mvc.MvcExceptionHandler



Jika Anda adalah seorang pengembang c # backend, Anda mungkin cepat atau lambat harus menemukan cara terpadu untuk menangani situasi luar biasa. Meskipun, bahkan jika Anda puas dengan 500 kode dalam jawaban, artikel ini masih akan membantu meningkatkan metode Anda, sementara tidak memaksa apa pun untuk menulis ulang.

Kami akan berbicara tentang pustaka ASP.NET, yang memungkinkan Anda untuk menyelesaikan masalah ini seanggun mungkin. Bagi mereka yang terlalu malas untuk membaca artikel yang panjang - readme dan perpustakaan itu sendiri ada di sini , contohnya ada di sini . Tersedia di nuget.org dan saya hanya akan senang jika itu bermanfaat bagi siapa pun. Jadi, mari kita beralih ke kode. Pertama, mari kita lihat alternatifnya.

Salah satu hal pertama yang mungkin terlintas dalam pikiran adalah membuat DTO (objek transfer data) untuk menangani pengecualian, menangkap pengecualian pada pengontrol (meskipun tidak perlu bahwa itu akan menjadi pengecualian, dimungkinkan untuk hanya memeriksa nol atau sesuatu seperti itu), Isi data di DTO dan kirimkan ke klien. Kode untuk metode ini mungkin terlihat seperti ini:

public IActionResult Get() { try { //Code with exception. } catch (Exception ex) { return new JsonResult( new ErrorDto { IsError = true, Message = ex.Message }); } } 

Pilihan lain adalah menggunakan kode status HTTP untuk ini.

 public IActionResult Get() { try { //Code with exception. } catch (Exception ex) { return BadRequest(); } } 

Praktik yang cukup umum, tetapi memiliki kelemahan: sulit untuk menggambarkan esensi situasi Anda dengan salah satu kode standar, itulah sebabnya kode yang sama dapat ditafsirkan berbeda bahkan pada sistem yang sama, dan juga memberikan informasi yang terlalu sedikit untuk debugging ke pengembang.

Dan di sini beberapa bahkan mungkin mulai menggabungkan kedua metode ini, dan dalam proporsi yang berbeda. Di suatu tempat mereka akan lupa untuk mengirim DTO, di suatu tempat kode tidak akan dikirim atau yang salah akan dikirim, tetapi di suatu tempat secara umum akan diserialisasi dengan pengaturan json yang salah dan akan kembali tidak apa yang dibutuhkan.

Menghadapi hal di atas, banyak yang mencoba untuk menyelesaikan masalah ini menggunakan app.UseExceptionHandler (); dengan menangani pengecualian melalui itu. Ini adalah upaya yang baik, tetapi tidak akan membiarkan Anda dengan mudah melupakan masalahnya. Pertama, Anda masih akan menghadapi masalah dalam memilih DTO untuk pengecualian. Kedua, penangan seperti itu tidak akan mengizinkan untuk memproses kode kesalahan http yang dikembalikan dari pengontrol, karena Pengecualian tidak terjadi. Ketiga, dengan cara ini tidak nyaman untuk menyelesaikan masalah klasifikasi kesalahan, Anda harus menulis banyak kode untuk melampirkan pesan, kode http atau sesuatu untuk setiap pengecualian. Dan keempat, Anda kehilangan kesempatan untuk menggunakan AspA DeveloperExceptionPage, yang sangat tidak nyaman untuk debugging. Bahkan jika Anda entah bagaimana menyelesaikan masalah ini, maka semua pengembang pada proyek ini harus benar-benar mengikuti spesifikasi, membangun penanganan kesalahan secara khusus pada pengecualian, jangan mengembalikan DTO mereka, jika tidak kesalahan dalam api Anda mungkin terlihat berbeda dari metode ke metode.

Pilihan penanganan pengecualian yang dipilih


Sebelum saya menunjukkan bagaimana IRO.Mvc.MvcExceptionHandler memungkinkan Anda untuk menangani pengecualian, pertama saya akan menjelaskan bagaimana saya melihat penanganan pengecualian yang ideal. Untuk melakukan ini, kami menetapkan sejumlah persyaratan:

  1. Seharusnya DTO, tetapi kami juga tidak menolak kode http, karena untuk banyak kesalahan, mereka masih sangat cocok, dapat digunakan di mana-mana dan dalam proyek lama yang harus Anda dukung juga, dan mereka cukup universal. DTO standar akan mencakup bidang IsError (yang memungkinkan penulisan penanganan kesalahan universal pada klien), juga harus berisi kode kesalahan string ErrorKey, yang dapat segera dikenali oleh pengembang hanya dengan melihatnya dan menyediakan informasi lebih lanjut. Selain itu, Anda dapat menambahkan tautan ke halaman dengan deskripsi kesalahan ini, jika perlu.
  2. Ini semua ada di prod. Dalam mode pengembangan, DTO ini harus mengembalikan jejak tumpukan, meminta data: cookie, header, parameter. Ke depan, middleware yang dijelaskan dalam artikel bahkan mengembalikan tautan ke DeveloperExceptionPage yang dihasilkan, yang memungkinkan Anda untuk menonton jejak pengecualian dalam bentuk yang mudah, tetapi lebih lanjut tentang itu nanti.
  3. Pengembang dapat mengikat pengecualian, kode kesalahan http, dan ErrorKey. Ini berarti bahwa jika ia mengirim kode 403 dari controller, maka jika pengembang melampirkan ErrorKey khusus untuk itu, DTO dengan itu akan dikembalikan. Dan sebaliknya, jika UnauthorizedAccessException terjadi, itu akan terikat pada kode http dan ErrorKey.

Ini adalah format default yang digunakan di perpustakaan:

 { "__IsError": true, "ErrorKey": "ClientException", "InfoUrl": "https://iro.com/errors/ClientException" } 

Saya harus mengatakan segera bahwa bentuk di mana data akan dikirim ke klien dapat benar-benar ada, ini hanya salah satu opsi.

IRO.Mvc.MvcExceptionHandler


Sekarang saya akan menunjukkan bagaimana saya memecahkan masalah ini untuk diri saya sendiri dengan menulis perpustakaan IRO.Mvc.MvcExceptionHandler.

Kami menghubungkan handler pengecualian seperti middleware lainnya - di kelas Startup.

 app.UseMvcExceptionHandler((s) => { //Settings... }); 

Di dalam delegasi yang sedang didelegasikan, kita perlu mengkonfigurasi middleware kita. Hal ini diperlukan untuk memetakan (mengikat) pengecualian pada kode http dan ErrorKey. Di bawah ini adalah opsi pengaturan yang paling mudah.

  s.Mapping((builder) => { builder.RegisterAllAssignable<Exception>( httpCode: 500, errorKeyPrefix: "Ex_" ); }); 

Seperti yang saya janjikan kepada pengembang hardcore paling malas yang tidak terbiasa menangani pengecualian - tidak ada lagi yang perlu dilakukan. Kode ini akan mengikat semua pengecualian dalam pipa ASP.NET ke DTO umum dengan kode 500, dan nama pengecualian akan ditulis ke ErrorKey.

Penting untuk dipahami bahwa metode RegisterAllAssignable tidak hanya mendaftarkan pengecualian dari tipe yang ditentukan, tetapi juga semua turunannya. Jika Anda hanya ingin mengirim informasi tentang pengecualian khusus kepada klien, itu adalah keputusan yang sepenuhnya masuk akal untuk membuat ClientException Anda dan memetakannya saja. Pada saat yang sama, jika Anda menetapkan satu kode http untuk ClientException, dan menetapkan kode lain untuk penerusnya SpecialClientException, maka kode SpecialClientException akan digunakan untuk semua keturunannya, mengabaikan pengaturan ClientException. Semua ini di-cache, sehingga tidak akan ada masalah kinerja.

Anda dapat menyempurnakan dan mendaftarkan kode ErrorKey dan http Anda untuk pengecualian tertentu:

  s.Mapping((builder) => { //By exception, custom error key. builder.Register<ArgumentNullException>( httpCode: 555, errorKey: "CustomErrorKey" ); //By http code. builder.Register( httpCode: 403, errorKey: "Forbidden" ); //By exception, default ErrorKey and http code. builder.Register<NullReferenceException>(); //Alternative registration method. builder.Register((ErrorInfo) new ErrorInfo() { ErrorKey = "MyError", ExceptionType = typeof(NotImplementedException), HttpCode = 556 }); }); 

Selain pemetaan, ada baiknya mengkonfigurasi middleweights. Anda dapat menentukan pengaturan serialisasi json, alamat situs Anda, tautan ke halaman deskripsi kesalahan, mode operasi middleware melalui IsDebug, kode http standar untuk pengecualian tanpa penanganan.

  s.ErrorDescriptionUrlHandler = new FormattedErrorDescriptionUrlHandler("https://iro.com/errors/{0}"); s.IsDebug = isDebug; s.DefaultHttpCode = 500; s.JsonSerializerSettings.Formatting = Formatting.Indented; s.Host="https://iro.com"; s.CanBindByHttpCode = true; 

Properti terakhir menunjukkan apakah mungkin untuk mengikat DTO kami dengan kode http.
Anda juga dapat menentukan cara menangani situasi dengan pengecualian internal, misalnya TaskCanceledException dengan kesalahan internal yang dicatat karena. Tunggu (). Misalnya, berikut ini adalah resolver standar yang mengeluarkan pengecualian internal dari pengecualian tersebut dan sudah bekerja dengannya:

  s.InnerExceptionsResolver = InnerExceptionsResolvers.InspectAggregateException; 

Jika Anda perlu menyempurnakan serialisasi, Anda dapat mengatur metode FilterAfterDTO. Kembalikan benar untuk menonaktifkan pemrosesan standar dan membuat serialisasi errorContext.ErrorDTO yang Anda inginkan. Ada akses ke HttpContext dan kesalahan itu sendiri.

  s.FilterAfterDTO = async (errorContext) => { //Custom error handling. Return true if MvcExceptionHandler must ignore current error, //because it was handled. return false; }; 

DeveloperExceptionPage dan keuntungan lain dari mode debug


Kami telah menemukan pengaturannya, sekarang mari kita cari tahu cara men-debug semuanya. Di prod DTO, jawaban dalam jawabannya sederhana dan saya sudah menunjukkannya di atas, sekarang saya akan menunjukkan bagaimana DTO yang sama terlihat dalam mode debug:



Seperti yang Anda lihat, ada banyak informasi lebih lanjut di sini, ada stackrace dan data permintaan. Tetapi bahkan lebih nyaman untuk hanya mengikuti tautan di bidang DebugUrl dan melihat data kesalahan tanpa melelahkan:



Agak sulit untuk mengimplementasikan fungsi ini, karena DeveloperExceptionPage tidak dimaksudkan untuk digunakan oleh pengembang pihak ketiga. Awalnya, tidak mungkin untuk membuka tautan di browser dengan sesi yang berbeda, konten tidak lagi ditampilkan setelah reboot. Semua ini bisa diselesaikan hanya dengan caching respons html dari perantara ini. Sekarang Anda setidaknya dapat mengirimkan tautan pengecualian ke rekan setim Anda jika Anda menggunakan server khusus yang dibagikan.

Kesimpulan


Saya harap para pengembang yang membaca artikel ini telah menemukan alat yang menarik dan bermanfaat untuk diri mereka sendiri. Bagi saya, artikel ini sebagian merupakan ujian dari kegunaan perkembangannya dan artikel tentang mereka. Saya memiliki beberapa proyek keren yang siap pakai yang ingin saya sampaikan kepada komunitas Habr.

Source: https://habr.com/ru/post/id469731/


All Articles