Setelah meninjau artikel Penanganan Pengecualian dalam API Web ASP.NET, saya agak bingung kapan harus melempar pengecualian vs mengembalikan respons kesalahan. Saya juga bertanya-tanya apakah mungkin untuk mengubah respons ketika metode Anda mengembalikan model domain tertentu daripada HttpResponseMessage
...
Jadi, untuk rekap di sini adalah pertanyaan saya diikuti oleh beberapa kode dengan case #s:
Pertanyaan
Pertanyaan tentang Kasus # 1
- Haruskah saya selalu menggunakan
HttpResponseMessage
model domain yang konkret, agar pesannya dapat dikustomisasi? - Bisakah pesan dikustomisasi jika Anda mengembalikan model domain konkret?
Pertanyaan tentang Kasus # 2,3,4
- Haruskah saya melempar pengecualian atau mengembalikan respons kesalahan? Jika jawabannya "tergantung", dapatkah Anda memberikan situasi / contoh kapan harus menggunakan satu vs yang lain.
- Apa perbedaan antara melempar
HttpResponseException
vsRequest.CreateErrorResponse
? Output ke klien tampaknya sama ... - Haruskah saya selalu menggunakan
HttpError
untuk "membungkus" pesan respons dalam kesalahan (apakah pengecualian dilemparkan atau respons kesalahan dikembalikan)?
Sampel Kasus
// CASE #1
public Customer Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
//var response = Request.CreateResponse(HttpStatusCode.OK, customer);
//response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return customer;
}
// CASE #2
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
// CASE #3
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
throw new HttpResponseException(errorResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
// CASE #4
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
Memperbarui
Untuk membantu lebih lanjut mendemonstrasikan kasus # 2,3,4 cuplikan kode berikut ini menyoroti beberapa opsi yang "dapat terjadi" ketika seorang pelanggan tidak ditemukan ...
if (customer == null)
{
// which of these 4 options is the best strategy for Web API?
// option 1 (throw)
var notFoundMessage = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundMessage);
// option 2 (throw w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
throw new HttpResponseException(errorResponse);
// option 3 (return)
var message = String.Format("Customer with id: {0} was not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
// option 4 (return w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}
Jawaban:
Pendekatan yang saya ambil adalah dengan hanya melemparkan pengecualian dari tindakan pengontrol api dan mendaftarkan filter pengecualian yang memproses pengecualian dan menetapkan respons yang sesuai pada konteks eksekusi tindakan.
Filter memperlihatkan antarmuka yang lancar yang menyediakan sarana untuk mendaftarkan penangan untuk jenis pengecualian tertentu sebelum mendaftarkan filter dengan konfigurasi global.
Penggunaan filter ini memungkinkan penanganan pengecualian terpusat alih-alih menyebarkannya di seluruh tindakan pengontrol. Namun ada kasus-kasus di mana saya akan menangkap pengecualian dalam aksi pengontrol dan mengembalikan respons tertentu jika tidak masuk akal untuk memusatkan penanganan penanganan pengecualian khusus itu.
Contoh pendaftaran filter:
Kelas UnhandledExceptionFilterAttribute:
Kode sumber juga dapat ditemukan di sini .
sumber
Jika Anda tidak mengembalikan HttpResponseMessage dan sebagai gantinya mengembalikan kelas entitas / model secara langsung, pendekatan yang menurut saya bermanfaat adalah menambahkan fungsi utilitas berikut ke controller saya.
dan cukup menyebutnya dengan kode status dan pesan yang sesuai
sumber
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid Request Format!"))
, tetapi di Fiddler, itu menunjukkan status 500 (bukan 400). Ada yang tahu kenapa?Kasus 1
Kasing # 2-4
Ini harus setara; HttpResponseException merangkum HttpResponseMessage, yang akan dikembalikan sebagai respons HTTP.
misalnya, case # 2 dapat ditulis ulang sebagai
... tetapi jika logika pengontrol Anda lebih rumit, melempar pengecualian mungkin menyederhanakan aliran kode.
HttpError memberi Anda format yang konsisten untuk badan respons dan dapat diserialisasi ke JSON / XML / etc, tetapi itu tidak diperlukan. misalnya, Anda mungkin tidak ingin memasukkan entitas-badan dalam respons, atau Anda mungkin menginginkan beberapa format lain.
sumber
Jangan melempar HttpResponseException atau mengembalikan HttpResponesMessage untuk kesalahan - kecuali jika tujuannya adalah untuk mengakhiri permintaan dengan hasil yang tepat .
HttpResponseException tidak ditangani sama dengan pengecualian lainnya . Mereka tidak terperangkap dalam Filter Pengecualian . Mereka tidak terperangkap dalam Exception Handler . Mereka adalah cara licik untuk memasukkan HttpResponseMessage sambil menghentikan aliran eksekusi kode saat ini.
Kecuali jika kode tersebut adalah kode infrastruktur yang mengandalkan penanganan khusus yang tidak ditangani ini, hindari menggunakan jenis HttpResponseException!
HttpResponseMessage bukan pengecualian. Mereka tidak menghentikan aliran eksekusi kode saat ini. Mereka tidak bisa difilter sebagai pengecualian. Mereka tidak dapat dicatat sebagai pengecualian. Mereka mewakili hasil yang valid - bahkan respons 500 adalah "respons non-pengecualian yang valid"!
Jadikan hidup lebih sederhana:
Ketika ada kasus luar biasa / kesalahan, lanjutkan dan lemparkan pengecualian .NET yang normal - atau jenis pengecualian aplikasi yang disesuaikan ( tidak berasal dari HttpResponseException) dengan properti 'kesalahan / respons' yang diinginkan seperti kode status - sesuai dengan pengecualian normal penanganan .
Gunakan Pengecualian Filter / Pengecualian Pengecualian / Pencatat Eksepsi untuk melakukan sesuatu yang sesuai dengan kasus luar biasa ini: mengubah / menambah kode status? tambahkan pengidentifikasi pelacakan? termasuk jejak tumpukan? catatan?
Dengan menghindari HttpResponseException , penanganan 'kasus luar biasa' dibuat seragam dan dapat ditangani sebagai bagian dari pipa yang terbuka! Misalnya seseorang dapat mengubah 'NotFound' menjadi 404 dan 'ArgumentException' menjadi 400 dan 'NullReference' menjadi 500 dengan mudah dan seragam dengan pengecualian tingkat aplikasi - sambil memungkinkan ekstensibilitas untuk memberikan "dasar-dasar" seperti kesalahan logging.
sumber
ArgumentException
s di controller secara logis menjadi 400, tapi bagaimana denganArgumentException
s lebih dalam stack? Itu tidak selalu benar untuk mengubah ini menjadi 400, namun jika Anda memiliki filter yang mengubah selimut semuaArgumentException
menjadi 400, satu-satunya cara untuk menghindari itu adalah dengan menangkap pengecualian di controller dan melempar kembali sesuatu yang lain, yang tampaknya untuk mengalahkan tujuan penanganan pengecualian seragam dalam filter atau serupa.Kasus lain kapan harus digunakan
HttpResponseException
alih-alihResponse.CreateResponse(HttpStatusCode.NotFound)
, atau kode status kesalahan lainnya, adalah jika Anda memiliki transaksi dalam filter tindakan dan Anda ingin transaksi tersebut dibatalkan saat mengembalikan respons kesalahan ke klien.Menggunakan
Response.CreateResponse
tidak akan mengembalikan transaksi, sedangkan melemparkan pengecualian akan.sumber
Saya ingin menunjukkan bahwa sudah pengalaman saya bahwa jika melempar HttpResponseException alih-alih mengembalikan HttpResponseMessage dalam metode webapi 2, bahwa jika panggilan dilakukan segera ke IIS Express, ia akan kehabisan waktu atau mengembalikan 200 tetapi dengan kesalahan html di responnya. Cara termudah untuk menguji ini adalah dengan membuat $ .ajax panggilan ke metode yang melempar HttpResponseException dan di errorCallBack di ajax membuat panggilan langsung ke metode lain atau bahkan halaman http sederhana. Anda akan melihat panggilan langsung akan gagal. Jika Anda menambahkan break point atau settimeout () di panggilan kesalahan kembali untuk menunda panggilan kedua satu atau dua detik, memberikan waktu server untuk memulihkannya berfungsi dengan benar.Memperbarui:Akar penyebab timeout koneksi Ajax aneh adalah jika panggilan ajax dibuat cukup cepat koneksi tcp yang sama digunakan. Saya meningkatkan 401 kesalahan eter dengan mengembalikan HttpResonseMessage atau melempar HTTPResponseException yang dikembalikan ke panggilan browser ajax. Tetapi bersamaan dengan panggilan itu, MS mengembalikan Objek Tidak Ditemukan Kesalahan karena dalam aplikasi Startup.Auth.vb.UserCookieAuthentication diaktifkan sehingga berusaha untuk kembali mencegat respons dan menambahkan arahan ulang tetapi kesalahan dengan Obyek bukan Instance dari Obyek. Kesalahan ini adalah html tetapi ditambahkan ke respons setelah fakta jadi hanya jika panggilan ajax dibuat cukup cepat dan koneksi tcp yang sama digunakan kembali ke browser dan kemudian ditambahkan ke depan panggilan berikutnya. Untuk beberapa alasan Chrome hanya batas waktu, Fiddler memilih karena campuran json dan htm tetapi firefox membalik kesalahan sebenarnya. Sangat aneh tetapi packet sniffer atau firefox adalah satu-satunya cara untuk melacak yang satu ini.
Juga harus dicatat bahwa jika Anda menggunakan bantuan Web API untuk menghasilkan bantuan otomatis dan Anda mengembalikan HttpResponseMessage maka Anda harus menambahkan
atribut ke metode ini sehingga bantuan menghasilkan dengan benar. Kemudian
atau kesalahan
Semoga ini bisa membantu orang lain yang mungkin mendapatkan batas waktu acak atau server tidak tersedia segera setelah melempar HttpResponseException.
Juga mengembalikan HttpResponseException memiliki manfaat tambahan yaitu tidak menyebabkan Visual Studio merusak pengecualian yang tidak ditangani, berguna ketika kesalahan dikembalikan adalah AuthToken perlu di-refresh dalam satu halaman aplikasi.
Pembaruan: Saya mencabut pernyataan saya tentang waktu habis IIS Express, ini kebetulan merupakan kesalahan di sisi klien saya ajax panggilan balik ternyata sejak Ajax 1,8 mengembalikan $ .ajax () dan mengembalikan $ .ajax. (). Then () keduanya mengembalikan janji tetapi tidak dirantai janji yang sama kemudian () mengembalikan janji baru yang menyebabkan urutan eksekusi salah. Jadi, ketika janji () selesai, itu adalah batas waktu skrip. Gotcha aneh tapi bukan masalah IIS express masalah antara Keyboard dan kursi.sumber
Sejauh yang saya tahu, apakah Anda melempar pengecualian, atau Anda mengembalikan Request.CreateErrorResponse, hasilnya sama. Jika Anda melihat kode sumber untuk System.Web.Http.dll, Anda akan melihat sebanyak itu. Lihatlah ringkasan umum ini, dan solusi yang sangat mirip yang telah saya buat: Web Api, HttpError, dan perilaku pengecualian
sumber
Dalam situasi kesalahan, saya ingin mengembalikan kelas detail kesalahan tertentu, dalam format apa pun yang diminta klien alih-alih objek happy path.
Saya ingin agar metode pengontrol saya mengembalikan objek path domain khusus dan melemparkan pengecualian.
Masalah yang saya miliki adalah bahwa konstruktor HttpResponseException tidak mengizinkan objek domain.
Inilah yang akhirnya saya temukan
Result
adalah kelas yang berisi detail kesalahan, sementara ituProviderCollection
adalah hasil jalur bahagia saya.sumber
saya suka jawaban oposisi
Lagi pula, saya membutuhkan cara untuk menangkap Pengecualian yang diwarisi dan solusi itu tidak memenuhi semua kebutuhan saya.
Jadi saya akhirnya mengubah cara dia menangani OnException dan ini adalah versi saya
Inti adalah loop ini di mana saya memeriksa apakah tipe pengecualian adalah subkelas dari tipe terdaftar.
my2cents
sumber