ASP.NET Web API: Cara yang benar untuk mengembalikan respons 401 / tidak sah

99

Saya memiliki situs MVC webapi yang menggunakan otentikasi OAuth / token untuk mengotentikasi permintaan. Semua pengontrol yang relevan memiliki atribut yang benar, dan otentikasi berfungsi dengan baik.

Masalahnya adalah tidak semua permintaan dapat diotorisasi dalam cakupan atribut - beberapa pemeriksaan otorisasi harus dilakukan dalam kode yang dipanggil oleh metode pengontrol - apa cara yang benar untuk mengembalikan respons 401 tidak sah dalam kasus ini?

Saya telah mencoba throw new HttpException(401, "Unauthorized access");, tetapi ketika saya melakukan ini, kode status respons adalah 500 dan saya juga mendapatkan jejak tumpukan. Bahkan dalam pendataan DelegatingHandler, kita dapat melihat bahwa responnya adalah 500, bukan 401.

GoatInTheMachine
sumber
1
Kepada siapa pun yang mengambil jawaban ini di telepon, saya sarankan untuk memikirkan waktu yang tepat untuk melempar HttpResponseExceptionversus kapan harus mengembalikan Unauthorized(). Menggunakan pengecualian untuk kesalahan 'yang diharapkan' adalah sedikit anti-pola, jadi jika ada kasus Anda mengharapkan panggilan membuat kesalahan ini, kembali Unauthorized()mungkin adalah panggilan yang tepat. Simpan HttpResponseExceptionuntuk hal yang benar-benar tidak terduga.
Rikki
Lihat github.com/aspnet/Mvc/issues/5507 untuk beberapa diskusi.
Rikki
@Rikki, 401 bukanlah kesalahan yang "diharapkan". - Ini adalah keadaan luar biasa yang seharusnya menyebabkan Anda membatalkan alur kerja Anda (kecuali mungkin untuk logging, yang seharusnya sudah Anda lakukan untuk pengecualian apa pun ...) - Bagaimanapun, jika Anda ingin mengembalikan hasil yang diketik dengan kuat dari pengontrol Anda ( misalnya untuk kemudahan pengujian unit), Pengecualian jelas merupakan rute terbaik.
BrainSlugs83

Jawaban:

145

Anda harus membuang HttpResponseExceptiondari metode API Anda, bukan HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Atau, jika Anda ingin memberikan pesan khusus:

var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "Oops!!!" };
throw new HttpResponseException(msg);
LukeH
sumber
97

Kembalikan saja yang berikut ini:

return Unauthorized();
JohnWrensby
sumber
3
Saya pikir diterima menjawab pertanyaan OP secara khusus. Jawaban saya menjawab judul pertanyaan "ASP.NET Web API: Cara yang benar untuk mengembalikan respons 401 / tidak sah"
JohnWrensby
3
Ada yang tahu mengapa tidak ada versi yang kelebihan muatan ini dengan pesan?
Simon_Weaver
5
@Simon_Weaver Tidak tahu kenapa, tapi Anda bisa menggunakan a return Content<string>(HttpStatusCode.Unauthorized, "Message");untuk melakukan ini.
Rikki
2
Ini harus menjadi jawaban yang benar. 1 itu benar. 2) Jika ini berubah dalam kerangka kerja selanjutnya, Anda tidak perlu mengubah kode. 3) Anda tidak perlu memberikan alasan untuk 401. Ini harus ditangani oleh klien dan bukan server.
Nick Turner
1
Di perpustakaan mana ini?
Nae
19

Sebagai alternatif jawaban lain, Anda juga dapat menggunakan kode ini jika Anda ingin mengembalikan IActionResultdalam pengontrol ASP.NET.

ASP.NET

 return Content(HttpStatusCode.Unauthorized, "My error message");

Pembaruan: ASP.NET Core

Kode di atas tidak berfungsi di ASP.NET Core, Anda dapat menggunakan salah satu dari berikut ini:

 return StatusCode((int)System.Net.HttpStatusCode.Unauthorized, "My error message");
 return StatusCode(401, "My error message");

Rupanya frasa alasan cukup opsional ( Dapatkah respons HTTP menghilangkan Frase-Alasan? )

Alex AIT
sumber
1
Ini tidak lagi bekerja di ASP.NET Core, ControllerBasekelas (digunakan oleh ASP.NET Core WebAPI) tidak lagi memiliki Contentkelebihan beban yang menerima kode status HTTP.
Dai
Ini salah. Tanggapan konten adalah status 200 Oke. Server harus mengirim 401 dan klien harus menangani yang sesuai. Anda tidak dapat mengirim 200 sebagai 401. Ini tidak masuk akal. Jika klien mendapatkan 401, itu bukan Ups, itu adalah pelanggaran hukum Anda.
Nick Turner
Kode ini mengirimkan kode status 401 ( HttpStatusCode.Unauthorized), bukan 200. Content(...)hanya singkatan untuk mengembalikan konten tertentu dengan kode status HTTP tertentu. Jika Anda ingin mengirim 200 Anda dapat menggunakanOk(...)
Alex AIT
@NickTurner - itu adalah argumen untuk metode webapi2 Content () yang diberi nama buruk bukan karena ini jawaban yang salah. Karena metode (status, pesan) diubah namanya di NetCore, saya kira para pengembang setuju bahwa itu dinamai dengan buruk.
Chris F Carroll
9

Anda mendapatkan kode respon 500 karena Anda melontarkan pengecualian (the HttpException) yang menunjukkan beberapa jenis kesalahan server, ini adalah pendekatan yang salah.

Cukup atur kode status respons .eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;
DGibbs
sumber
Agak aneh jika pengecualian mengambil kode status HTTP sebagai parameter, dan dokumen intellisense mengatakan bahwa ini adalah kode status yang dikirim ke klien - saya berharap untuk menghindari mutasi respons sendiri secara langsung karena ini tampaknya rawan kesalahan, mengingat status globalnya
GoatInTheMachine
1
Pengontrol API Web dasar tidak mengekspos Responseproperti.
LukeH
3

Untuk menambah jawaban yang ada di ASP.NET Core> = 1.0 Anda bisa

return Unauthorized();

return Unauthorized(object value);

Untuk meneruskan info ke klien, Anda dapat melakukan panggilan seperti ini:

return Unauthorized(new { Ok = false, Code = Constants.INVALID_CREDENTIALS, ...});

Pada klien selain respons 401, Anda juga akan mendapatkan data yang diteruskan. Misalnya pada kebanyakan klien Anda bisa await response.json()mendapatkannya.

Gabriel P.
sumber
3

Dalam .Net Core Anda dapat menggunakan

return new ForbidResult();

dari pada

return Unauthorized();

yang memiliki keuntungan untuk mengalihkan ke halaman default yang tidak sah (Akun / AccessDenied) daripada memberikan 401 langsung

untuk mengubah lokasi default, ubah startup.cs Anda

services.AddAuthentication(options =>...)
            .AddOpenIdConnect(options =>...)
            .AddCookie(options =>
            {
                options.AccessDeniedPath = "/path/unauthorized";

            })
mattbloke
sumber
Pertanyaannya adalah tentang API web. Jadi ini akan menjadi jawaban yang tidak valid jika tidak salah? API tidak boleh mengembalikan 'tindakan', hanya hasil.
Niels Lucas
1

Anda dapat menggunakan kode ikuti di asp.net core 2.0:

public IActionResult index()
{
     return new ContentResult() { Content = "My error message", StatusCode = (int)HttpStatusCode.Unauthorized };
}
AminRostami
sumber
1

Anda juga mengikuti kode ini:

var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
      Content = new StringContent("Users doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
      StatusCode = HttpStatusCode.NotFound
 }
 throw new HttpResponseException(response);
Kamrul Hasan
sumber
Anda tidak perlu mengatur StatusCode lagi jika Anda meneruskannya ke konstruktor - menggunakan salah satunya boleh saja
Jon Story