API REST 404: URI xấu hoặc thiếu tài nguyên?


219

Tôi đang xây dựng API REST, nhưng tôi đã gặp sự cố.

Có vẻ như thực tế được chấp nhận trong việc thiết kế API REST là nếu tài nguyên được yêu cầu không tồn tại, 404 sẽ được trả về.

Tuy nhiên, với tôi, điều này thêm sự mơ hồ không cần thiết. HTTP 404 thường được liên kết với một URI xấu. Vì vậy, trong thực tế, chúng tôi đang nói "Hoặc bạn đã đến đúng nơi, nhưng hồ sơ cụ thể đó không tồn tại, hoặc không có vị trí nào như vậy trên Internets! Tôi thực sự không chắc chắn cái nào ..."

Hãy xem xét URI sau:

http://mywebsite/api/user/13

Nếu tôi lấy lại 404, có phải vì Người dùng 13 không tồn tại? Hoặc là vì URL của tôi phải là:

http://mywebsite/restapi/user/13

Trước đây, tôi vừa trả về kết quả NULL với HTTP 200 OKmã phản hồi nếu bản ghi không tồn tại. Nó đơn giản, và theo tôi là rất sạch sẽ, ngay cả khi nó không nhất thiết phải được chấp nhận thực hành. Nhưng có cách nào tốt hơn để làm điều này?


Có lẽ là một bản sao. stackoverflow.com/questions/3821663/
Mạnh

7
Câu hỏi khác dường như có liên quan đến các định dạng chuỗi Truy vấn URI. Cuộc thảo luận về 404 không đủ để trả lời câu hỏi của tôi, đó là liệu có cách nào phù hợp hoặc hữu ích hơn để xác định ý nghĩa của 404 thực sự là gì không. Tôi đã xem lại cái đó trước khi đăng.
Brian Lacy

Có phải là bình thường khi concole trình duyệt bao gồm các lỗi 404? Khi tôi làm các hoạt động thường xuyên với uri chính xác nhưng không tìm thấy tài nguyên.
Vasiliy Mazhekin

3
404 không chỉ ra URI xấu, nó chỉ ra tài nguyên Không tìm thấy. Đó có thể là do không có người dùng '13' hoặc có thể là do không có tài nguyên / mywebsite / api.
ChrisV

Câu trả lời:


101

404 chỉ là mã phản hồi HTTP. Trên hết, bạn có thể cung cấp phần thân phản hồi và / hoặc các tiêu đề khác với thông báo lỗi có ý nghĩa hơn mà các nhà phát triển sẽ thấy.


7
Điều này thật ý nghĩa. Tuy nhiên, tôi phải tự hỏi, liệu có bất kỳ lợi ích nào thực sự thu được từ việc trả lại 404 ở vị trí đầu tiên, so với trả lại 200 với phản hồi không?
Brian Lacy

15
Nếu bạn trả về 200 với giá trị null, bạn sẽ đối đầu với HTTP Spec. Bạn có thể làm điều này, nhưng sau đó api của bạn sẽ không tuân thủ ràng buộc "Giao diện thống nhất" của REST.
kiện

5
... và cơ quan phản hồi của bạn nên bao gồm các siêu liên kết đến các URI hợp lệ. Chặn URI gốc và có thể là một hoặc hai dấu trang, khách hàng của bạn phải luôn theo dõi các liên kết được cung cấp cho họ bởi máy chủ. Sau đó, không cần phải phát minh ra ngữ nghĩa chi tiết về chính xác cách họ quyết định làm việc bên ngoài hệ thống.
fumanchu

@DarrylHebbes có nghĩa là gì, tôi có thể thực hiện một yêu cầu và xem nội dung đầy đủ của phản hồi trong tab Mạng của bảng điều khiển dành cho nhà phát triển Chrome.
jaapz

59

Sử dụng 404nếu tài nguyên không tồn tại. Đừng trở về 200với một cơ thể trống rỗng.

Điều này gần giống undefinedvới chuỗi rỗng (ví dụ "") trong lập trình. Trong khi rất giống nhau, chắc chắn có một sự khác biệt.

404có nghĩa là không có gì tồn tại ở URI đó (giống như một biến không xác định trong lập trình). Trở về 200với một cơ thể trống rỗng có nghĩa là một cái gì đó tồn tại ở đó và một cái gì đó chỉ trống ngay bây giờ (giống như một chuỗi trống trong lập trình).

404không có nghĩa đó là "URI xấu". Có các mã HTTP đặc biệt dành cho các lỗi URI (ví dụ 414 Request-URI Too Long).


1
Hey, tôi nghĩ rằng bạn có thể vào một cái gì đó. Sẽ không có ý nghĩa hơn khi trả lại một trong số "41?" lỗi khi có vấn đề với tài nguyên được yêu cầu? Ví dụ, 410 Gone có nghĩa là "Tài nguyên được yêu cầu không còn khả dụng tại máy chủ và không có địa chỉ chuyển tiếp nào được biết đến." - (Xem w3.org/Prot Protocol / rfc2616 / rfc2616-sec10.html # sec10.4.11 )
Brian Lacy

Trên thực tế, tài nguyên mà tôi tham chiếu ở trên cũng cho biết: "Nếu máy chủ không biết hoặc không có cơ sở để xác định, liệu điều kiện có vĩnh viễn hay không, mã trạng thái 404 (Không tìm thấy) NÊN sử dụng thay thế." Vì vậy, có lẽ đó cũng không hẳn là lựa chọn tốt nhất ..
Brian Lacy

2
Tôi không nghĩ bất kỳ lỗi nào trong 41x là phù hợp với trường hợp sử dụng của bạn. Tôi thích so sánh với chuỗi không xác định so với chuỗi rỗng, đây là phiên bản ngắn gọn hơn về quan điểm của tôi trong câu trả lời của tôi. Có một sự khác biệt, nhưng điều đó không ngụ ý rằng một chuỗi rỗng là một lỗi. Để kéo dài sự tương tự: Bạn có thể có một phương thức String getName()có triển khai như thế này : return this.name == null ? "" : this.name. Đó không phải là không chính xác, trừ khi bạn muốn các khách hàng để biết rằng this.namenull.
jhericks

1
404 vẫn là lựa chọn thích hợp nhất. Bạn có thể (và được khuyến khích) sử dụng phần thân của phản hồi 404 đó để thông báo cho người dùng / khách hàng về các chi tiết cụ thể để nhận lỗi. Xem: stackoverflow.com/a/9999335/105484 . Trong trường hợp của bạn, bạn có thể muốn sử dụng cơ thể đó để đề xuất với người dùng rằng họ định dạng lại URI của họ để trông giống như / restapi / user / USER_ID
nargetood

Ok, tôi có thể thấy một số điểm tốt ở đây, nhưng 4XX là mã lỗi, đó là một tình huống lỗi như thế nào? Làm thế nào khách hàng có thể ngăn 404 xảy ra?
Krzysztof Kubicki

20

Như với hầu hết mọi thứ, "nó phụ thuộc". Nhưng với tôi, cách luyện tập của bạn không tệ và sẽ không đi ngược lại với thông số HTTP mỗi lần . Tuy nhiên, hãy làm rõ một số điều.

Đầu tiên, URI phải mờ đục. Ngay cả khi chúng không mờ đối với con người, chúng vẫn mờ đục đối với máy móc. Nói cách khác, sự khác biệt giữa http://mywebsite/api/user/13, http://mywebsite/restapi/user/13giống như sự khác biệt giữa http://mywebsite/api/user/13http://mywebsite/api/user/14tức là không giống nhau không cùng thời kỳ. Vì vậy, một 404 sẽ hoàn toàn phù hợp cho http://mywebsite/api/user/14(nếu không có người dùng như vậy) nhưng không nhất thiết là phản hồi thích hợp duy nhất.

Bạn cũng có thể trả về 200 phản hồi trống hoặc rõ ràng hơn là phản hồi 204 (Không có nội dung). Điều này sẽ truyền đạt một cái gì đó khác cho khách hàng. Nó có nghĩa là tài nguyên được xác định bởi http://mywebsite/api/user/14không có nội dung hoặc về cơ bản là không có gì. Nó không có nghĩa rằng có một nguồn tài nguyên như vậy. Tuy nhiên, điều đó không nhất thiết có nghĩa là bạn cho rằng có một số người dùng vẫn tồn tại trong kho lưu trữ dữ liệu với id 14. Đó là mối quan tâm riêng tư của bạn, không phải là mối quan tâm của khách hàng khi đưa ra yêu cầu. Vì vậy, nếu nó hợp lý để mô hình hóa tài nguyên của bạn theo cách đó, hãy tiếp tục.

Có một số ý nghĩa bảo mật trong việc cung cấp cho khách hàng của bạn thông tin sẽ giúp họ dễ dàng đoán URI hợp pháp hơn. Trả lại 200 lần bỏ lỡ thay vì 404 có thể cung cấp cho khách hàng một manh mối rằng ít nhất là http://mywebsite/api/userphần đó là chính xác. Một khách hàng độc hại có thể tiếp tục thử các số nguyên khác nhau. Nhưng với tôi, một khách hàng độc hại sẽ có thể đoán được http://mywebsite/api/userphần nào. Một biện pháp khắc phục tốt hơn sẽ là sử dụng UUID. tức http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66là tốt hơn so với http://mywebsite/api/user/14. Làm điều đó, bạn có thể sử dụng kỹ thuật trả lại 200 của bạn mà không cần cho đi nhiều.


1
Đây là giải pháp tôi đã chọn và sử dụng 204 thay vì 404. Đối với tôi 204 có nghĩa là "Tôi đã tìm thấy mã của bạn, nhưng không tìm thấy kết quả" và 404 có nghĩa là "Tôi không thể tìm thấy bất kỳ mã nào để thực thi, phải không con đường sai? "
Matt Sanders

11

404 Không tìm thấy về mặt kỹ thuật có nghĩa là uri hiện không ánh xạ tới tài nguyên. Trong ví dụ của bạn, tôi diễn giải một yêu cầu http://mywebsite/api/user/13trả về 404 để ngụ ý rằng url này không bao giờ được ánh xạ tới tài nguyên. Đối với khách hàng, đó nên là kết thúc của cuộc trò chuyện.

Để giải quyết mối quan tâm với sự mơ hồ, bạn có thể nâng cao API của mình bằng cách cung cấp các mã phản hồi khác. Ví dụ: giả sử bạn muốn cho phép khách hàng phát hành các yêu cầu GET http://mywebsite/api/user/13, bạn muốn truyền đạt rằng khách hàng nên sử dụng url chuẩn http://mywebsite/restapi/user/13. Trong trường hợp đó, bạn có thể muốn xem xét việc chuyển hướng vĩnh viễn bằng cách trả về 301 Đã di chuyển vĩnh viễn và cung cấp url chuẩn trong tiêu đề Vị trí của phản hồi. Điều này cho khách hàng biết rằng đối với các yêu cầu trong tương lai, họ nên sử dụng url chuẩn.


10

Vì vậy, về bản chất, có vẻ như câu trả lời có thể phụ thuộc vào cách yêu cầu được hình thành.

Nếu tài nguyên được yêu cầu tạo thành một phần của URI theo yêu cầu http://mywebsite/restapi/user/13và người dùng 13 không tồn tại, thì 404 có thể phù hợp và trực quan vì URI là đại diện của người dùng / thực thể / tài liệu / không tồn tại. Điều tương tự cũng sẽ áp dụng cho kỹ thuật an toàn hơn bằng cách sử dụng GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66và đối số api / restapi ở trên.

Tuy nhiên, nếu ID tài nguyên được yêu cầu được bao gồm trong tiêu đề yêu cầu [bao gồm ví dụ của riêng bạn] hoặc thực tế, trong URI dưới dạng tham số, ví dụ: http://mywebsite/restapi/user/?UID=13URI vẫn sẽ chính xác (vì khái niệm NGƯỜI DÙNG vẫn tồn tại http://mywebsite/restapi/user/); và do đó, phản hồi hợp lý có thể được dự kiến ​​là 200 (với thông điệp dài dòng thích hợp) vì người dùng cụ thể được gọi là 13 không tồn tại nhưng URI thì có. Theo cách này, chúng tôi đang nói URI là tốt, nhưng yêu cầu dữ liệu không có nội dung.

Cá nhân 200 vẫn không cảm thấy đúng (mặc dù trước đây tôi đã lập luận như vậy). Mã phản hồi 200 (không có phản hồi dài dòng) có thể khiến vấn đề không được điều tra khi ID không chính xác được gửi chẳng hạn.

Một cách tiếp cận tốt hơn sẽ là gửi một 204 - No Contentphản hồi. Điều này phù hợp với mô tả của w3c * Máy chủ đã thực hiện yêu cầu nhưng không cần trả về một thực thể và có thể muốn trả về thông tin được cập nhật. * 1 Sự nhầm lẫn, theo tôi là do mục nhập Wikipedia nêu 204 Không có Nội dung - Máy chủ đã xử lý thành công yêu cầu, nhưng không trả lại bất kỳ nội dung nào. Thường được sử dụng như một phản hồi cho một yêu cầu xóa thành công .Câu cuối cùng rất dễ gây tranh cãi. Xem xét tình huống mà không có câu đó và giải pháp rất dễ dàng - chỉ cần gửi 204 nếu thực thể không tồn tại. Thậm chí còn có một đối số để trả về 204 thay vì 404, yêu cầu đã được xử lý và không có nội dung nào được trả về! Tuy nhiên, xin lưu ý rằng 204 không cho phép nội dung trong phần phản hồi

Nguồn

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Prot Protocol / rfc2616 / rfc2616-sec10.html


9

Đó là một bài viết rất cũ nhưng tôi đã đối mặt với một vấn đề tương tự và tôi muốn chia sẻ kinh nghiệm của tôi với các bạn.

Tôi đang xây dựng kiến ​​trúc microservice với các API còn lại. Tôi có một số dịch vụ GET còn lại, họ thu thập dữ liệu từ hệ thống back-end dựa trên các tham số yêu cầu.

Tôi đã theo dõi các tài liệu thiết kế API còn lại và tôi đã gửi lại HTTP 404 với thông báo lỗi JSON hoàn hảo cho khách hàng khi không có dữ liệu nào phù hợp với các điều kiện truy vấn (ví dụ: không có bản ghi nào được chọn).

Khi không có dữ liệu để gửi lại cho khách hàng, tôi đã chuẩn bị một thông báo JSON hoàn hảo với mã lỗi nội bộ, v.v. để thông báo cho khách hàng về lý do của "Không tìm thấy" và nó đã được gửi lại cho khách hàng với HTTP 404. Điều đó hoạt động tốt

Sau đó, tôi đã tạo một lớp máy khách API còn lại, một trình trợ giúp dễ dàng để ẩn mã liên quan đến giao tiếp HTTP và tôi đã sử dụng trình trợ giúp này mọi lúc khi tôi gọi các API còn lại từ mã của mình.

NHƯNG tôi cần phải viết thêm mã khó hiểu chỉ vì HTTP 404 có hai chức năng khác nhau:

  • HTTP 404 thực khi API còn lại không có sẵn trong url đã cho, nó được ném bởi máy chủ ứng dụng hoặc máy chủ web nơi ứng dụng API còn lại chạy
  • khách hàng cũng nhận lại HTTP 404 khi không có dữ liệu trong cơ sở dữ liệu dựa trên điều kiện truy vấn.

Quan trọng: Trình xử lý lỗi API còn lại của tôi nắm bắt tất cả các ngoại lệ xuất hiện trong dịch vụ back-end, nghĩa là trong trường hợp có bất kỳ lỗi nào, API còn lại của tôi luôn trả về với một thông báo JSON hoàn hảo với các chi tiết thông báo.

Đây là phiên bản đầu tiên của phương thức trợ giúp khách hàng của tôi xử lý hai phản hồi HTTP 404 khác nhau:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

NHƯNG , bởi vì máy khách Java hoặc JavaScript của tôi có thể nhận được hai loại HTTP 404 bằng cách nào đó tôi cần kiểm tra phần thân của phản hồi trong trường hợp HTTP 404. Nếu tôi có thể phân tích phần thân phản hồi thì tôi chắc chắn rằng tôi đã nhận được phản hồi khi có không có dữ liệu để gửi lại cho khách hàng

Nếu tôi không thể phân tích cú pháp phản hồi, điều đó có nghĩa là tôi đã lấy lại HTTP 404 thực từ máy chủ web (không phải từ ứng dụng API còn lại).

Điều này thật khó hiểu và ứng dụng khách luôn cần phải phân tích cú pháp thêm để kiểm tra lý do thực sự của HTTP 404.

Thành thật mà nói tôi không thích giải pháp này. Thật khó hiểu, cần phải thêm mã nhảm nhí cho khách hàng mọi lúc.

Vì vậy, thay vì sử dụng HTTP 404 trong hai kịch bản khác nhau này, tôi đã quyết định rằng tôi sẽ làm như sau:

  • Tôi không sử dụng HTTP 404 làm mã HTTP phản hồi trong ứng dụng còn lại của mình nữa.
  • Tôi sẽ sử dụng HTTP 204 (Không có nội dung) thay vì HTTP 404.

Trong trường hợp đó, mã khách hàng có thể thanh lịch hơn:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

Tôi nghĩ rằng điều này xử lý vấn đề đó tốt hơn.

Nếu bạn có bất kỳ giải pháp tốt hơn xin vui lòng chia sẻ nó với chúng tôi.


Các phương thức ApacheClClient không ném ngoại lệ vào phản hồi 204. Nếu khách hàng của bạn chỉ trả lời các đối tượng kinh doanh (mô hình), thì điều đó sẽ trả về null. Hoạt động, nhưng khó khăn cho người dùng cuối để phát hiện tình huống 204.
chrisinmtown

Tất cả đều tốt, nhưng một câu hỏi là: bạn có thường xuyên viết nếu các câu lệnh cho 404 không? Và nó sẽ là gì? Mặt khác, nếu bạn thực hiện cuộc gọi đến api, điều đó xác định 404 có thể xảy ra với lỗi json bên trong, bạn có thể xử lý nó chỉ trong trường hợp này.
Tối đa

Tôi đồng ý với bạn. Tôi có một dịch vụ trả về dữ liệu. Nhưng có những tình huống dữ liệu bị hỏng. URL yêu cầu là chính xác (vì vậy không phải là 404), nhưng nó không thực sự là lỗi logic (500), vì vậy 204 có vẻ phù hợp nhất. Nó thực sự gây phiền nhiễu về việc thiếu một thứ cơ thể tin nhắn. Mặc dù có thể cho rằng tôi có thể chèn một lý do vào Tiêu đề.
cs94njw

6

Bài viết cũ nhưng tuyệt vời này ... http://www.infoq.com/articles/webber-rest-workflow nói điều này về nó ...

404 Không tìm thấy - Dịch vụ quá lười biếng (hoặc bảo mật) để cung cấp cho chúng tôi lý do thực sự tại sao yêu cầu của chúng tôi không thành công, nhưng dù lý do là gì, chúng tôi cần phải giải quyết.


1

Mã định danh tài nguyên đồng nhất là một con trỏ duy nhất cho tài nguyên. Một URI dạng kém không trỏ đến tài nguyên và do đó thực hiện GET trên nó sẽ không trả về tài nguyên. 404 có nghĩa là Máy chủ không tìm thấy bất cứ thứ gì khớp với URI yêu cầu. Nếu bạn đặt sai URI hoặc URI xấu, đó là vấn đề của bạn và lý do bạn không nhận được tài nguyên cho dù là trang HTML hay IMG.


0

Đối với kịch bản này, HTTP 404 là mã phản hồi cho phản hồi từ API REST Giống như 400, 401, 404, 422 thực thể không thể xử lý

sử dụng xử lý Ngoại lệ để kiểm tra thông báo ngoại lệ đầy đủ.

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

Khối ngoại lệ này cung cấp cho bạn thông báo phù hợp được gửi bởi API REST

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.