Thiết kế API RESTful. Tôi nên trả lại những gì nếu không có hàng?


50

Tôi hiện đang mã hóa API cho mạng xã hội với Slim Framework. Câu hỏi của tôi là: các thực tiễn tốt nhất khi không có hàng để trả về trong cấu trúc json là gì?

Hãy nói rằng cuộc gọi / v1 / get / phim này trả về 2 hàng từ tên phim bảng:

[
    {"name": "Ghostbusters"},
    {"name": "Indiana Jones"}
]

Nhưng, sau đó tôi gọi / v1 / get / sách và không có hàng nào trong bảng đó. Tôi có nên trả lại một cấu trúc trống?

[
]

... Hoặc sẽ tốt hơn một tin nhắn và mã lỗi?

[
    "errors": {
        "message": "no matches found",
        "code": 134
    }
]

Đó là một thực hành tốt hơn? (API sẽ được sử dụng trong ứng dụng iOS và Android) Cảm ơn!


3
Đối với tôi điều này giống như câu hỏi liệu số không thực sự là một số tiền.
Scarfridge

16
Ví dụ của bạn bị hỏng. Bạn không thể có một đối tượng json với các khóa trùng lặp. Những gì bạn đang tìm kiếm là một mảng, tức là[{"name": "..."}, {"name":"..."}]
Martin Wickman

@MartinWickman Xin lỗi vì điều đó, tôi chỉ sửa nó.
Andres SK

8
@andufo, thực ra, bạn đã không ...
avakar

25
Nếu ứng dụng của bạn có nghĩa là RESTful, thì tại sao động từ / phương thức lại "lấy" một phần của URI điểm cuối của bạn?
user50849

Câu trả lời:


46

Thông thường tôi sẽ trả về số lượng hồ sơ trong kết quả là siêu dữ liệu. Tôi không chắc đó có phải là thực hành REST bình thường không, nhưng nó không có nhiều dữ liệu bổ sung, và nó rất chính xác. Thông thường có phân trang cho rất nhiều dịch vụ, việc trả lại kết quả khổng lồ cùng một lúc là không thực tế. Cá nhân tôi cảm thấy khó chịu khi có phân trang cho các tập kết quả nhỏ .. Nếu nó trống, trả về number_of_records : 0và sách dưới dạng danh sách / mảng trống books : [].

{
    meta: {
        number_of_records : 2,
        records_per_page : 10,
        page : 0
    },
    books : [
        {id:1},
        {id:27}
    ]
}

EDIT (vài năm sau): Câu trả lời từ Martin Wickman tốt hơn nhiều, đây là "ngắn gọn" giải thích tại sao.

Khi xử lý phân trang luôn ghi nhớ khả năng nội dung hoặc thay đổi thứ tự. Giống như, yêu cầu đầu tiên đến, 24 kết quả, bạn trả lại đầu tiên 10. Sau đó, "cuốn sách mới" được chèn và bây giờ bạn có 25 kết quả, nhưng với yêu cầu ban đầu, nó sẽ được đặt ở vị trí thứ 10. Khi người dùng đầu tiên yêu cầu trang thứ 2, anh ta sẽ không nhận được "cuốn sách mới". Có nhiều cách để xử lý các vấn đề như vậy, như cung cấp "id yêu cầu" cần được gửi bằng các lệnh gọi API sau, sau đó trả lại trang tiếp theo từ tập kết quả "cũ", được lưu trữ bằng cách nào đó và được gắn với "id yêu cầu". Thay thế là thêm trường như "danh sách kết quả đã thay đổi kể từ yêu cầu đầu tiên".

Nói chung, nếu bạn có thể, hãy cố gắng nỗ lực thêm và tránh phân trang. Phân trang là trạng thái bổ sung có thể bị thay đổi và theo dõi các thay đổi như vậy dễ bị lỗi, thậm chí còn nhiều hơn vì cả máy chủ và máy khách đều cần xử lý.

Nếu bạn có quá nhiều dữ liệu để xử lý cùng một lúc , hãy xem xét trả lại "danh sách id" với tất cả các kết quả và chi tiết cho một số phần của danh sách đó và cung cấp các lệnh gọi API multi_get / get_by_id_list cho tài nguyên.


1
Huh, tôi tự hỏi tại sao cái này không được bình chọn cao như cái kia. Tôi thích điều này hơn vì nó đưa ra cả một danh sách trống (đáng lẽ là một danh sách, phải không?) Mà bạn có thể lặp đi lặp lại một cách mù quáng mà không cần điều kiện đặc biệt, nhưng cũng có siêu dữ liệu như một cách nói, "Không, chúng tôi đã không ' Không có lỗi cho bạn, thực tế đã có 0 kết quả ".
Izkata

1
-1 vì bookstham số là một đối tượng nhưng 'sách' ngụ ý nhiều hơn một và nhiều hơn một mảng ngụ ý. Dữ liệu meta rất tuyệt và cuối cùng tôi mong đợi một bộ sách sẽ là một loạt các đối tượng sách; nếu không có sách chỉ cho tôi mảng trống
Charles Sprayberry

9
Vấn đề với điều này là việc thêm "number_of_records" không cung cấp thêm thông tin nào, nó chỉ thêm sự dư thừa và tăng độ phức tạp. Để báo hiệu lỗi, trả lại mã http phù hợp + nội dung nào đó trong phần thân.
Martin Wickman

1
Sách @cspray là danh sách, như Izkata chỉ, lỗi đánh máy của tôi.
grizwako

2
@MartinWickman Tôi không muốn làm ô nhiễm câu trả lời gốc bằng siêu dữ liệu bổ sung, nhưng theo kinh nghiệm của tôi, rất nhiều dịch vụ không trả lại tất cả dữ liệu ngay lập tức mà theo cách "phân trang".
grizwako

105

Ví dụ của bạn bị hỏng. Bạn không nên có các đối tượng json với các khóa trùng lặp. Những gì bạn đang tìm kiếm là một mảng với các đối tượng phim, như thế này:

 [
    {"name": "movie1"}, 
    {"name": "movie2"}
 ]

Cách tiếp cận này cũng trả lời câu hỏi của bạn. Bạn nên trả về một mảng trống khi truy vấn không khớp:

[]

Mặt khác, nếu bạn cố lấy tài nguyên phim cụ thểGET api/movie/34 và phim đó không tồn tại, thì hãy trả về 404 với thông báo lỗi (mã hóa json) phù hợp trong phần thân


1
+1 Đây là JSON hợp lệ theo json_xs.
l0b0

15

Nếu đây là JSON, bạn thực sự nên xem xét trả về một mảng các đối tượng. Điều này có nhiều lợi thế bao gồm cả khi bạn không có bản ghi thì đó là một mảng trống.

Vì vậy, khi bạn có hồ sơ, bạn sẽ trở lại:

    [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]

Và khi bạn không có hồ sơ, bạn sẽ trở lại:

    [

    ]

14

Nếu bạn thực hiện thao tác thành công, nhưng nó không có gì để trả về, chẳng hạn như bản đồ {}trống hoặc mảng trống []tôi muốn phản hồi với mã phản hồi 204, đây là đoạn trích từ thông số Định nghĩa Mã trạng thái HTTP :

Máy chủ đã hoàn thành 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. Phản hồi CÓ THỂ bao gồm các thông số mới hoặc cập nhật dưới dạng các tiêu đề thực thể, nếu hiện tại NÊN được liên kết với biến thể được yêu cầu.

Nếu khách hàng là tác nhân người dùng, nó KHÔNG NÊN thay đổi chế độ xem tài liệu của mình từ đó gây ra yêu cầu được gửi. Phản hồi này chủ yếu nhằm cho phép đầu vào cho các hành động diễn ra mà không gây ra thay đổi đối với chế độ xem tài liệu đang hoạt động của tác nhân người dùng, mặc dù mọi thông số mới hoặc cập nhật NÊN được áp dụng cho tài liệu hiện tại trong chế độ xem hoạt động của tác nhân người dùng.

Phản hồi 204 PHẢI KHÔNG bao gồm phần thân thông điệp và do đó luôn bị chấm dứt bởi dòng trống đầu tiên sau các trường tiêu đề.

Về cơ bản, tôi khuyên bạn nên sử dụng 204 trong các ứng dụng RESTful qua HTTP khi không có gì để trả về.


4
Tôi đồng ý với nhận xét của @ avakar về câu trả lời khác ở đây. Nếu khách hàng đang cố truy cập / v1 / get / movies / 1 thì nó sẽ trả về 404 nếu không có phim nào có thể nhận dạng bằng 1. Chỉ / v1 / get / phim sẽ trả về 200 ngay cả khi không có phim. Nhưng 204 không phù hợp vì nó dành cho các hành động đầu vào.
imel96

7
Một vấn đề khác với giải pháp này là nó thường sẽ cần mã đặc biệt trong máy khách: Nếu phản hồi là một danh sách trống, nó có thể được phân tích cú pháp dưới dạng JSON giống như một phản hồi bình thường. Nếu phản hồi là phần thân trống, trình phân tích cú pháp JSON có thể sẽ khiếu nại (vì tài liệu emtpy không phải là JSON hợp lệ), vì vậy khách hàng cần thêm mã để kiểm tra HTTP 204 và bỏ qua phân tích cú pháp.
sleske

7
Tôi tin rằng đây là một sự hiểu sai về ý định của 204. 204 dường như không nhằm mục đích cho các hoạt động dự kiến ​​nội dung và không tìm thấy bất kỳ, mà là cho các hoạt động thành công và không có ý định trở lại . Từ Wikipedia: "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 làm phản hồi cho yêu cầu xóa thành công."
XML

10

Đã có một lượng công việc hợp lý được thực hiện để tạo định dạng API JSON được tiêu chuẩn hóa .

Theo các nguyên tắc trong đặc tả đó có nghĩa là tất cả các tài nguyên được trả về phải thực sự là "các bộ sưu tập" (ngay cả khi chỉ bao gồm một tài nguyên duy nhất). Theo dõi điều này có nghĩa là cuộc gọi của bạn /v1/get/moviessẽ trở lại:

{
    "movies": [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]
}

Cuộc gọi của bạn đến /v1/get/books(trả về không tài nguyên) sẽ trả về:

{
    "books": []
}

5

Đối với ví dụ cụ thể của bạn, tôi khuyên bạn nên / v1 / get / sách nên trả về HTTP 200 với một mảng trống.

Nếu tôi đang đọc đúng bài đăng của mình, API của bạn dự định sẽ thu thập sách. Nói một cách ẩn dụ, bạn có một giá sách cho sách, giá DVD cho phim và có thể các hộp đựng khác mà bạn chưa đề cập ở đây. Bởi vì bạn có ý định thu thập sách, / v1 / get / sách là giá sách của bạn. Điều này có nghĩa là có một tài nguyên hợp lệ ở đó - một danh sách các sách - có thể trống trong ví dụ cụ thể của bạn.

Lý do tôi không khuyên bạn nên trả lại HTTP 404 trong trường hợp này là giá sách vẫn còn đó. Hiện tại không có bất kỳ cuốn sách nào về nó, nhưng nó vẫn là một giá sách. Nếu đó không phải là giá sách - nếu API không có ý định thu thập sách, chẳng hạn - thì HTTP 404 sẽ phù hợp. Nhưng vì có tài nguyên ở đó, bạn không nên báo hiệu rằng không có tài nguyên, HTTP 404 nào. Do đó, tôi lập luận rằng 200 với một mảng trống (biểu thị bộ sưu tập) là phù hợp hơn.

Lý do tôi không đề xuất trả lại HTTP 204 là vì điều này sẽ gợi ý rằng "Không có nội dung" là trạng thái thông thường: thực hiện hành động này trên tài nguyên này thông thường sẽ không trả lại bất cứ điều gì. Đó là lý do tại sao nó thường được sử dụng như một phản hồi cho các yêu cầu XÓA, ví dụ: bản chất của việc xóa thường có nghĩa là không còn gì để trả về. Trường hợp tương tự khi nó được sử dụng để đáp ứng các yêu cầu với họ tiêu đề If-Modified: bạn chỉ muốn nội dung nếu tài nguyên đã thay đổi, nhưng nó không có, vì vậy tôi sẽ không cung cấp cho bạn bất kỳ nội dung nào.

Nhưng tôi lập luận rằng để NHẬN một bộ sưu tập trống nhưng hợp lệ, HTTP 204 không có ý nghĩa gì. Nếu có các mục trong bộ sưu tập, thì biểu diễn thích hợp sẽ là một mảng của dữ liệu đó. Do đó, khi không có dữ liệu ở đó (nhưng bộ sưu tập hợp lệ), biểu diễn thích hợp là một mảng trống.


5

Bạn thực sự chỉ nên làm một trong hai điều

Trả về 200 (OK)mã trạng thái và một mảng trống trong phần thân.

Hoặc Trả lại 204 (NO CONTENT)mã trạng thái và KHÔNG phản hồi cơ thể.

Đối với tôi, tùy chọn 2 có vẻ đúng về mặt kỹ thuật hơn và tuân thủ các nguyên tắc REST và HTTP.

Tuy nhiên, tùy chọn 1 có vẻ hiệu quả hơn cho máy khách - vì máy khách không cần thêm logic để phân biệt giữa hai mã trạng thái (thành công). Vì nó biết rằng nó sẽ luôn nhận được một mảng, nên nó chỉ cần kiểm tra xem nó có không, một hay nhiều mục và xử lý nó một cách thích hợp không


3

Tôi đã thấy cả hai trường hợp trong môi trường sản xuất. Cái nào bạn chọn phụ thuộc vào người sẽ sử dụng API. Nếu họ muốn biết lý do tại sao danh sách trống hoặc để chắc chắn rằng danh sách đó thực sự trống và không có lỗi xảy ra trong khi truy xuất nó, thì bạn nên đính kèm một đối tượng "lỗi". Nếu họ không quan tâm, hãy trả lại một danh sách trống. Tôi sẽ đi theo cách tiếp cận thứ hai vì nó đáp ứng nhiều nhu cầu hơn so với cách tiếp cận thứ nhất.


3

Điều đầu tiên cần xem xét, vì bạn đang xây dựng API RESTful, là trả về mã phản hồi thích hợp. Và mã phản hồi phù hợp hơn để thông báo rằng yêu cầu đã đi qua bình thường, nhưng tài nguyên được yêu cầu hiện không có sẵn là 404 đáng kính.

Nếu bạn thiết kế API của mình theo cách nó luôn trả về mã phản hồi hợp lý, bạn thậm chí có thể không cần trả về phần thân khi không tìm thấy tài nguyên. Điều đó nói rằng, trả lại một cơ thể, đặc biệt là một người có thể đọc được, không thể làm tổn thương.

Không có "thực hành tốt nhất" ở đây, cả hai ví dụ của bạn là tùy ý, chỉ cần chọn một và nhất quán . Các nhà phát triển ghét sự bất ngờ, nếu /v1/get/moviestrở lại {}khi không có phim thì chúng tôi cũng mong sẽ /v1/get/actorstrở lại {}khi không có diễn viên.


1
Trả lại 404 thực sự là điều nên làm, nhưng thật đáng buồn là không ai thực sự làm điều đó - bao gồm cả bản thân tôi.
RibaldEddie

1
nếu bạn có phản hồi phức tạp và chỉ một phần trong số đó trống, việc trả lại 404 sẽ khiến người dùng bối rối.
hết sức

5
Tôi sẽ không đồng ý với tin nhắn 404. 404 Tôi sẽ hiểu là "tài nguyên không tồn tại" và lo lắng nếu tôi gặp vấn đề gì với URL của mình hoặc bất cứ điều gì. Nếu tôi yêu cầu danh sách phim và nhận 404, tôi sẽ nghĩ rằng không có tài nguyên phim nào cả. "204 Không có nội dung" có thể phù hợp hơn.
thorsten müller

8
Ok, thứ "không có cơ thể" sẽ giết chết nó. Nhưng: "Lớp mã trạng thái 4xx dành cho các trường hợp máy khách dường như bị lỗi.". Nhưng không có lỗi ở phía khách hàng. Vì vậy, 404 cung cấp thông tin sai. Hoặc gửi 204 mà không có cơ thể hoặc nói rằng nó ổn và gửi một danh sách trống.
thorsten müller

9
Bạn đang yêu cầu một danh sách các cuốn sách, trả về 404 có nghĩa là danh sách đó không tồn tại, không phải là nó trống. Trả lại 200 cùng với một danh sách trống dường như là lựa chọn hợp lý duy nhất với tôi.
avakar

1

Tôi không nghĩ câu trả lời cứng nhắc là câu được đánh dấu.

Câu trả lời được cung cấp bởi nirth nên là tốt nhất, trong một kịch bản REST thực sự. Phản hồi cơ thể phải trống và mã trạng thái http: 204; tài nguyên tồn tại nhưng nó "không có nội dung" tại thời điểm đó: trống rỗng.

REST HTTP_Status_Codes


1

Tôi khuyên dùng 200 + mảng trống, vì nó đơn giản hóa việc xử lý bởi tất cả các máy khách của API. 200 + mảng có nghĩa là "Tôi đã trả lại tất cả dữ liệu ở đó". Cả mã cung cấp dữ liệu và mã xử lý dữ liệu, số lượng mục sẽ không liên quan.

Mỗi mã trạng thái khác cần phải được ghi lại đúng cách và được phân phối đúng cách bởi máy chủ và được xử lý đúng bởi máy khách và tất cả chúng ta đều biết khả năng này xảy ra như thế nào.

Có một đề nghị để trả lại trạng thái 204 + cơ thể trống rỗng. Điều đó có nghĩa bạn buộc mỗi đơn khách hàng để tình trạng quá 204 một cách chính xác. Hơn nữa, bạn buộc họ phải xử lý các câu trả lời không phải JSON! Tôi hy vọng mọi người nhận ra rằng chỉ vì một yêu cầu đã có câu trả lời, điều đó không có nghĩa là câu trả lời đến từ máy chủ khi http được sử dụng và chỉ cần kiểm tra xem phản hồi là JSON xử lý nhiều trường hợp đó.


0

Tôi sẽ "Nó phụ thuộc".

Nếu không là kết quả hợp lý thì trả về danh sách trống. Ví dụ: nếu bạn muốn có được tất cả các nhân viên được gọi là "bob" trong đó "không" là kết quả khá hợp lý. Nếu đó không phải là kết quả mong đợi sẽ trả về lỗi. Ví dụ: nhận danh sách lịch sử các địa chỉ đường phố cho một người bạn tuyển dụng .. Họ phải sống ở một nơi nào đó để không có kết quả có thể là một lỗi, không chỉ là một điều kiện bình thường.

Tôi chắc rằng bạn có thể tranh luận với các chi tiết cụ thể của ví dụ của tôi, nhưng bạn hiểu ý ...


0
  • Trước hết, có gettrong URL của bạn không phải là RESTful, GET được ngụ ý bởi phương thức HTTP.
  • Nếu bạn đang yêu cầu một bộ sưu tập như GET api/moviestrả về một 200 OKmảng trống [].
  • Nếu bạn đang yêu cầu một bộ phim cụ thể như GET api/movies/1( 1id ở đâu) và nó không tồn tại, hãy trả lại a 404 Not Found.

Tại sao? Bạn đang yêu cầu tài nguyên . Khi bạn yêu cầu bộ sưu tập, tài nguyên (bộ sưu tập) tồn tại. Do đó, a 404là sai. Nhưng nếu bạn yêu cầu một bộ phim cụ thể và nó không tồn tại, tài nguyên được yêu cầu không tồn tại và bạn phải trả lại a 404.


-2

Nếu bạn đang trả về JSON, tốt hơn là luôn trả về số lượng và thông báo lỗi và có thể là Boolean chỉ ra nếu có lỗi hay không, đó là ba giá trị meta tiêu chuẩn của tôi được trả về với mỗi danh sách hàng.

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.