ASP.NET Web API: Cách đúng để trả về phản hồi 401 / trái phép


98

Tôi có trang webapi MVC sử dụng xác thực OAuth / mã thông báo để xác thực các yêu cầu. Tất cả các bộ điều khiển liên quan đều có thuộc tính phù hợp và xác thực đang hoạt động tốt.

Vấn đề là không phải tất cả yêu cầu đều có thể được ủy quyền trong phạm vi của một thuộc tính - một số kiểm tra ủy quyền phải được thực hiện trong mã được gọi bởi các phương thức bộ điều khiển - cách chính xác để trả về phản hồi 401 trái phép trong trường hợp này là gì?

Tôi đã thử throw new HttpException(401, "Unauthorized access");, nhưng khi tôi làm điều này, mã trạng thái phản hồi là 500 và tôi cũng nhận được dấu vết ngăn xếp. Ngay cả trong trình ghi nhật ký của chúng tôi, chúng tôi có thể thấy rằng phản hồi là 500, không phải 401.


1
Đối với bất kỳ ai chọn câu trả lời này dưới dòng, tôi khuyên bạn nên suy nghĩ về thời điểm thích hợp để ném một câu trả lời HttpResponseExceptionso với khi nào trả lại một câu trả lời Unauthorized(). Việc sử dụng ngoại lệ cho một lỗi 'mong đợi' là một chút sai lầm, vì vậy nếu có trường hợp bạn cho rằng cuộc gọi mắc lỗi này, thì việc quay lại Unauthorized()có lẽ là cuộc gọi phù hợp. Tiết kiệm HttpResponseExceptioncho những gì thực sự bất ngờ.
Rikki

Xem github.com/aspnet/Mvc/issues/5507 để biết một số thảo luận.
Rikki

@Rikki, 401 không phải là lỗi "mong đợi". - Đó là một trường hợp đặc biệt khiến bạn phải hủy bỏ quy trình làm việc của mình (ngoại trừ có thể ghi nhật ký, điều mà bạn nên làm vì bất kỳ trường hợp ngoại lệ nào ...) - Dù sao, nếu bạn muốn trả về một kết quả được đánh máy mạnh mẽ từ bộ điều khiển của mình ( ví dụ để dễ kiểm tra đơn vị), một Ngoại lệ rõ ràng là con đường tốt nhất.
BrainSlugs83

Câu trả lời:


144

Bạn nên ném một HttpResponseExceptiontừ phương thức API của mình, không phải HttpException:

throw new HttpResponseException(HttpStatusCode.Unauthorized);

Hoặc, nếu bạn muốn cung cấp một thông báo tùy chỉnh:

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

95

Chỉ cần trả lại như sau:

return Unauthorized();

2
Tôi nghĩ câu trả lời được chấp nhận cho câu hỏi của OP cụ thể. Câu trả lời của tôi trả lời cho tiêu đề của câu hỏi "ASP.NET Web API: Cách chính xác để trả lại 401 / phản hồi trái phép"
JohnWrensby

3
Có ai biết tại sao không có phiên bản quá tải này với một thông báo không?
Simon_Weaver

5
@Simon_Weaver Không biết tại sao, nhưng bạn có thể sử dụng a return Content<string>(HttpStatusCode.Unauthorized, "Message");để làm điều này.
Rikki

2
Đây phải là câu trả lời chính xác. 1 nó là chính xác. 2) Nếu điều này thay đổi trong khuôn khổ sau này, bạn không cần phải thay đổi mã. 3) Bạn không cần cung cấp lý do cho 401. Việc này sẽ được xử lý bởi máy khách chứ không phải máy chủ.
Nick Turner

1
Thư viện này ở trong thư viện nào?
Nae

19

Để thay thế cho các câu trả lời khác, bạn cũng có thể sử dụng mã này nếu bạn muốn trả về một IActionResultbộ điều khiển ASP.NET.

ASP.NET

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

Cập nhật: ASP.NET Core

Mã trên không hoạt động trong ASP.NET Core, bạn có thể sử dụng một trong những mã này để thay thế:

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

Rõ ràng cụm từ lý do là khá tùy chọn ( Phản hồi HTTP có thể bỏ qua Cụm từ lý do không? )


1
Điều này không còn hoạt động trong ASP.NET Core, ControllerBaselớp (được sử dụng bởi ASP.NET Core WebAPI) không còn Contentquá tải chấp nhận mã trạng thái HTTP.
Đại

Cái này sai. Phản hồi Nội dung là trạng thái 200 Ok. Máy chủ sẽ gửi 401 và máy khách sẽ xử lý tương ứng. Bạn không thể gửi 200 dưới dạng 401. Nó không hợp lý. Nếu khách hàng nhận được 401, đó không phải là Rất tiếc, đó là bạn vi phạm luật.
Nick Turner

Mã này đang gửi mã trạng thái 401 ( HttpStatusCode.Unauthorized), không phải 200. Content(...)chỉ đơn giản là cách viết tắt để trả về bất kỳ nội dung nhất định nào với mã trạng thái HTTP nhất định. Nếu bạn muốn gửi 200, bạn có thể sử dụngOk(...)
Alex AIT

@NickTurner - đó là đối số cho phương thức Webapi2 Content () được đặt tên kém không phải vì đây là câu trả lời sai. Vì phương thức (trạng thái, thông báo) được đổi tên trong NetCore, tôi đoán các nhà phát triển đồng ý rằng nó được đặt tên kém.
Chris F Carroll

9

Bạn nhận được mã phản hồi 500 vì bạn đang ném một ngoại lệ (cái HttpException) cho biết một số loại lỗi máy chủ, đây là cách tiếp cận sai.

Chỉ cần đặt mã trạng thái phản hồi .eg

Response.StatusCode = (int)HttpStatusCode.Unauthorized;

Hơi kỳ lạ khi ngoại lệ lấy mã trạng thái HTTP làm tham số và tài liệu intellisense nói rằng đây là mã trạng thái được gửi đến máy khách - tôi đã hy vọng tự mình tránh trực tiếp thay đổi phản hồi vì điều này có vẻ dễ xảy ra lỗi, như trạng thái toàn cầu của nó
GoatInTheMachine

1
Bộ điều khiển API Web cơ sở không hiển thị thuộc Responsetính.
LukeH

3

Để thêm vào câu trả lời hiện có trong ASP.NET Core> = 1.0, bạn có thể

return Unauthorized();

return Unauthorized(object value);

Để chuyển thông tin cho khách hàng, bạn có thể thực hiện một cuộc gọi như sau:

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

Trên máy khách, ngoài phản hồi 401, bạn cũng sẽ có dữ liệu được chuyển. Ví dụ trên hầu hết các khách hàng, bạn có thể await response.json()lấy nó.


3

Trong .Net Core Bạn có thể sử dụng

return new ForbidResult();

thay vì

return Unauthorized();

có lợi thế để chuyển hướng đến trang trái phép mặc định (Tài khoản / AccessDenied) thay vì đưa ra 401 ngay lập tức

để thay đổi vị trí mặc định, hãy sửa đổi startup.cs của bạn

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

            })

Câu hỏi là về một API web. Vì vậy, đây sẽ là một câu trả lời không hợp lệ nếu không sai? API không nên trả về 'hành động', chỉ trả về kết quả.
Niels Lucas

1

bạn có thể sử dụng mã sau trong asp.net core 2.0:

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

1

Bạn cũng làm theo mã này:

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);

Bạn không cần đặt lại Mã trạng thái nếu bạn chuyển nó cho hàm tạo - sử dụng một trong hai cũng được
Jon Story
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.