找不到資料要回 404 還是 200?

TLDR:當找不到目標資源或是不想讓別人知道資源存在的時候,可以回 404。除非團隊有額外的定義。

前情提要

近年來,RESTful 愛好者都會提出使用有意義的 HTTP 狀態碼(status code)來表達回傳內容當下的狀態。在找不到資料的情境時,過去的習慣是回傳 200,並帶有額外的錯誤碼:

{
"code": "C0001"
}

RESTful 愛好者「可能」會說回傳 200 是上一世紀的做法了,然後提出應該要使用 404 來表達「找不到資源」的意義,因此就開始吵架啦!

了解定義

先不要提 RESTful 這個名詞,因為它也是建立在 HTTP 的協定上,因此應該是先關注 HTTP 的協定說了什麼。

首先看 404 狀態碼的定義

The 404 (Not Found) status code indicates that the origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

404 想回應的狀態是,當目標資源(target resource,可以理解成 URI)找不到 current representation,或是不想讓別人知道資源存在。

後者很好理解,在 403 狀態碼的定義能找到說明:

An origin server that wishes to “hide” the current existence of a forbidden target resource MAY instead respond with a status code of 404 (Not Found).

當想「隱藏」目標資源已存在的狀態時,可以用 404 狀態碼來取代 403。

前者則有點複雜,因為它講了另一個概念 *Representation*:

A representation is information that is intended to reflect a past, current, or desired state of a given resource, in a format that can be readily communicated via the protocol.

Representation 反應給定資源的過去、現在或是期望的狀態的資訊。

  • 「現在的狀態」比方說取得資源時,請求 GET /users/123 如果存在,這就代表著現在的狀態(current representation)。
  • 「期望的狀態」比方說更新資源時,請求 PUT /users/123 可以傳入預期的狀態,成功後,資源「現在的狀態」會被更新,而預期的狀態就會變成「現在的狀態」。
  • 「過去的狀態」比方說刪除資源時,請求 DELETE /users/123 使用軟刪除(soft delete)後,設計可能是:GET 請求「現在的狀態」會找不到資源(404),但是後台的另一個 API 則可以取得資源「過去的狀態」。

然後裡面又提到了 representation 可以有多個表示方法:

A target resource might be provided with, or be capable of generating, multiple representations that are each intended to reflect the resource’s current state.

所以 RESTful 有提到同一個 API 可以回傳 JSON 格式或 XML 格式,而每個回傳代表的都是資源當前狀態,這是符合 HTTP 定義的。

查詢單筆資料

看到這裡,再回頭看一下最初的 404 定義:尋找不到 current representation 代表什麼意思呢?簡單來說有下面這兩種:

  1. 目標資源不存在,原因一是 current representation 沒找到,二是沒有定義目標資源。
  2. 目標資源存在,且 media type 合法(不合法會回 415),但找不到對應的 media type 的 representation。

這就能解釋為什麼在請求 GET /users/123 找不到資料的時候,要回的是 404--要嘛目標資源不存在,要嘛沒有合適的 representation。

查詢多筆資料

有個情境是,請求 GET /users 找所有或多筆使用者,如果找不到使用者時,應該要回 404 還是 200?

這時要看目標資源的 representation 是如何定義的。若是在找不到資料的時候,能夠對應出 representation,那就應該要回 200。以要查多筆使用者來說,資料通常會是以列表(list)型態輸出,空列表是合理的 representation,也能表達出資源現在的狀態,因此可以回應 200。

相對地,當單筆資料找不到時,若要回傳 200 與一個空的實體(entity),意思是指「資源現在的狀態存在,而實體是空的」。這件事是不合理的,因為資料不存在的話,是無法建立實體的。

結論

以 HTTP 協定規範來設計的話:

  • 查詢單筆找不到回 404
  • 查詢多筆找不到回 200 + 空列表

若團隊有自己規範的話,那就依照團隊規範來執行。

順帶一提:

找不到資料回 404 狀態碼,這是上古時代(1992 年 HTTP/0.9)的做法。

The server has not found anything matching the URI given