API REST Thực tiễn tốt nhất: Cách chấp nhận danh sách các giá trị tham số làm đầu vào [đã đóng]


409

Chúng tôi đang khởi chạy API REST mới và tôi muốn một số đầu vào của cộng đồng về các thực tiễn tốt nhất xung quanh cách chúng tôi nên định dạng các tham số đầu vào:

Ngay bây giờ, API của chúng tôi rất tập trung vào JSON (chỉ trả về JSON). Cuộc tranh luận về việc chúng tôi muốn / cần trả lại XML hay không là một vấn đề riêng biệt.

Vì đầu ra API của chúng tôi là trung tâm JSON, chúng tôi đã đi vào một con đường nơi đầu vào của chúng tôi là một trung tâm JSON và tôi đã nghĩ rằng có thể thuận tiện cho một số nhưng nói chung là kỳ lạ.

Ví dụ: để có được một vài chi tiết sản phẩm trong đó nhiều sản phẩm có thể được kéo cùng một lúc chúng tôi hiện có:

http://our.api.com/Product?id=["101404","7267261"]

Chúng ta có nên đơn giản hóa điều này như:

http://our.api.com/Product?id=101404,7267261

Hoặc có sẵn đầu vào JSON? Thêm một nỗi đau?

Chúng tôi có thể muốn chấp nhận cả hai phong cách nhưng sự linh hoạt đó có thực sự gây ra nhiều nhầm lẫn và đau đầu (khả năng bảo trì, tài liệu, v.v.) không?

Một trường hợp phức tạp hơn là khi chúng tôi muốn cung cấp đầu vào phức tạp hơn. Ví dụ: nếu chúng tôi muốn cho phép nhiều bộ lọc trên tìm kiếm:

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

Chúng tôi không nhất thiết muốn đặt các loại bộ lọc (ví dụ: ProductType và color) làm tên yêu cầu như thế này:

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Bởi vì chúng tôi muốn nhóm tất cả các bộ lọc đầu vào với nhau.

Cuối cùng, điều này thực sự quan trọng? Có thể có rất nhiều tiện ích JSON ngoài kia đến mức kiểu đầu vào không quan trọng lắm.

Tôi biết các máy khách JavaScript của chúng tôi thực hiện các cuộc gọi AJAX tới API có thể đánh giá cao các đầu vào JSON để làm cho cuộc sống của chúng dễ dàng hơn.

Câu trả lời:


341

Một bước lùi

Đầu tiên và quan trọng nhất, REST mô tả một URI là một ID duy nhất trên toàn cầu. Quá nhiều người bị cuốn vào cấu trúc của URI và URI nào "yên tĩnh" hơn những người khác. Lập luận này thật ngớ ngẩn khi nói việc đặt tên cho ai đó là "Bob" tốt hơn là đặt tên cho anh ấy là "Joe" - cả hai tên đều có công việc "xác định một người" được thực hiện. Một URI không có gì khác hơn là một tên duy nhất trên toàn cầu .

Vì vậy, trong mắt của REST tranh luận về việc liệu ?id=["101404","7267261"]có yên tĩnh hơn ?id=101404,7267261hay \Product\101404,7267261có phần vô ích.

Bây giờ, đã nói rằng, nhiều lần cách URI được xây dựng thường có thể đóng vai trò là một chỉ báo tốt cho các vấn đề khác trong dịch vụ RESTful. Có một vài lá cờ đỏ trong URI và câu hỏi của bạn nói chung.

Gợi ý

  1. Nhiều URI cho cùng một tài nguyên và Content-Location

    Chúng tôi có thể muốn chấp nhận cả hai phong cách nhưng sự linh hoạt đó có thực sự gây ra nhiều nhầm lẫn và đau đầu (khả năng bảo trì, tài liệu, v.v.) không?

    URI xác định tài nguyên. Mỗi tài nguyên nên có một URI chuẩn. Điều này không có nghĩa là bạn không thể có hai URI trỏ đến cùng một tài nguyên nhưng có nhiều cách được xác định rõ ràng để thực hiện nó. Nếu bạn quyết định sử dụng cả JSON và các định dạng danh sách dựa (hoặc bất kỳ định dạng khác), bạn cần phải quyết định các định dạng là chính kinh điển URI. Tất cả các phản hồi cho các URI khác trỏ đến cùng một "tài nguyên" phải bao gồm Content-Locationtiêu đề .

    Gắn bó với tên tương tự, có nhiều URI giống như có biệt danh cho mọi người. Điều này là hoàn toàn chấp nhận được và thường rất tiện dụng, tuy nhiên nếu tôi đang sử dụng một biệt danh, tôi vẫn có thể muốn biết tên đầy đủ của họ - cách "chính thức" để chỉ người đó. Theo cách này khi ai đó nhắc đến ai đó bằng tên đầy đủ của họ, "Nichloas Telsa", tôi biết họ đang nói về cùng một người mà tôi gọi là "Nick".

  2. "Tìm kiếm" trong URI của bạn

    Một trường hợp phức tạp hơn là khi chúng tôi muốn cung cấp đầu vào phức tạp hơn. Ví dụ: nếu chúng tôi muốn cho phép nhiều bộ lọc trên tìm kiếm ...

    Một nguyên tắc chung của tôi là, nếu URI của bạn chứa một động từ, nó có thể là một dấu hiệu cho thấy một cái gì đó bị tắt. URI xác định một tài nguyên, tuy nhiên họ không nên chỉ ra những gì chúng tôi đang làm với tài nguyên đó. Đó là công việc của HTTP hoặc trong điều kiện yên tĩnh, "giao diện thống nhất" của chúng tôi.

    Để đánh bại tên tương tự đã chết, sử dụng động từ trong URI giống như thay đổi tên của ai đó khi bạn muốn tương tác với họ. Nếu tôi đang tương tác với Bob, tên của Bob sẽ không trở thành "BobHi" khi tôi muốn nói Hi với anh ấy. Tương tự, khi chúng tôi muốn "tìm kiếm" Sản phẩm, cấu trúc URI của chúng tôi không nên thay đổi từ "/ Sản phẩm / ..." thành "/ Tìm kiếm / ...".

Trả lời câu hỏi ban đầu của bạn

  1. Về ["101404","7267261"]vs 101404,7267261: Đề xuất của tôi ở đây là tránh cú pháp JSON vì đơn giản (nghĩa là không yêu cầu người dùng của bạn thực hiện mã hóa URL khi bạn thực sự không phải làm vậy). Nó sẽ làm cho API của bạn trở nên hữu dụng hơn. Tốt hơn nữa, như những người khác đã khuyến nghị, hãy sử dụng application/x-www-form-urlencodedđịnh dạng chuẩn vì nó có thể sẽ quen thuộc nhất với người dùng cuối của bạn (ví dụ ?id[]=101404&id[]=7267261). Nó có thể không "đẹp", nhưng các URI đẹp không cần thiết có nghĩa là các URI có thể sử dụng được. Tuy nhiên, để nhắc lại quan điểm ban đầu của tôi, cuối cùng, khi nói về REST, điều đó không thành vấn đề. Đừng tập trung quá nhiều vào nó.

  2. Ví dụ URI tìm kiếm phức tạp của bạn có thể được giải quyết theo cách tương tự như ví dụ về sản phẩm của bạn. Tôi khuyên bạn nên đi application/x-www-form-urlencodedđịnh dạng lại vì nó đã là một tiêu chuẩn mà nhiều người quen thuộc. Ngoài ra, tôi sẽ khuyên bạn nên hợp nhất hai.

URI của bạn ...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

URI của bạn sau khi được mã hóa URI ...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Có thể biến thành ...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

Ngoài việc tránh yêu cầu mã hóa URL và làm cho mọi thứ trông chuẩn hơn một chút, giờ đây nó đồng nhất hóa API một chút. Người dùng biết rằng nếu họ muốn truy xuất Sản phẩm hoặc Danh sách Sản phẩm (cả hai đều được coi là một "tài nguyên" duy nhất theo thuật ngữ RESTful), họ quan tâm đến /Product/...URI.


67
Muốn theo dõi và lưu ý rằng []cú pháp không phải lúc nào cũng được hỗ trợ (và mặc dù nó phổ biến, thậm chí có thể vi phạm thông số URI). Một số máy chủ HTTP và ngôn ngữ lập trình sẽ thích chỉ lặp lại tên (ví dụ productType=value1&productType=value2).
chiến lược

1
Câu hỏi ban đầu với truy vấn này .. "/ Tìm kiếm? Term = pumas & bộ lọc = {" sản phẩm ": [" Quần áo "," Túi "]," màu ": [" Đen "," Đỏ "]}" dịch thành .. . (sản phẩm = = quần áo | | sản phẩm = = túi) && (màu == đen | = Màu đỏ dường như dịch sang ... Hoặc (sản phẩm = = quần áo | | sản phẩm = = túi | | màu == đen | | màu == đỏ) hoặc Either (sản phẩm = = quần áo && sản phẩm == túi && color == đen && color == red) Có vẻ hơi khác với tôi. Hay tôi đã hiểu lầm?
Thomas Cheng

2
Điều gì về đầu vào trong yêu cầu bài? Tôi muốn biết nếu chúng ta đang cập nhật một tài nguyên, thì việc gửi truy vấn / bộ lọc và dữ liệu trong phần thân theo định dạng chuẩn có phải là một thực tế không tốt. ví dụ nếu tôi muốn thay đổi dữ liệu liên quan đến người dùng bằng api /user/và trong phần thân, tôi sẽ gửi { q:{}, d: {} }với qtư cách truy vấn bằng người dùng sẽ được truy vấn trong DB và ddưới dạng dữ liệu đã sửa đổi.
phân tử

1
Bạn làm gì khi danh sách có thể rất lớn? URI bị giới hạn về độ dài tùy thuộc vào trình duyệt. Tôi thường đã chuyển sang một yêu cầu bài viết và gửi danh sách trong cơ thể. Có gợi ý nào không?
Troy Cosentino

4
Nó sẽ phải RẤT lớn (xem stackoverflow.com/questions/417142/ ), nhưng có, trong những trường hợp cực đoan nhất, bạn có thể cần phải sử dụng phần thân của yêu cầu. POST truy vấn để truy xuất dữ liệu là một trong những điều mà RESTafarians thích tranh luận.
chiến lược

233

Cách tiêu chuẩn để truyền danh sách các giá trị dưới dạng tham số URL là lặp lại chúng:

http://our.api.com/Product?id=101404&id=7267261

Hầu hết các mã máy chủ sẽ diễn giải điều này như một danh sách các giá trị, mặc dù nhiều mã có đơn giản hóa giá trị đơn để bạn có thể phải đi tìm.

Giá trị giới hạn cũng được.

Nếu bạn cần gửi JSON đến máy chủ, tôi không muốn thấy nó trong URL (đây là một định dạng khác). Cụ thể, các URL có giới hạn kích thước (trong thực tế nếu không phải trên lý thuyết).

Cách tôi đã thấy một số thực hiện một truy vấn phức tạp RESTly là hai bước:

  1. POST yêu cầu truy vấn của bạn, nhận lại ID (về cơ bản tạo tài nguyên tiêu chí tìm kiếm)
  2. GET tìm kiếm, tham khảo ID trên
  3. tùy chọn XÓA các yêu cầu truy vấn nếu cần, nhưng lưu ý rằng các yêu cầu đó có sẵn để sử dụng lại.

8
Cảm ơn Kathy. Tôi nghĩ rằng tôi đang ở bên bạn và cũng không thực sự muốn thấy JSON trong URL. Tuy nhiên, tôi không phải là người thích làm một bài đăng cho một tìm kiếm, đó là một hoạt động GET vốn có. Bạn có thể chỉ ra một ví dụ về điều này?
whatupwilly

1
Nếu các truy vấn có thể làm việc như các tham số đơn giản, hãy làm điều đó. Nguồn được lấy từ danh sách gửi thư thảo luận còn lại: tech.groups.yahoo.com/group/rest-discuss/message/11578
Kathy Van Stone

2
Nếu bạn chỉ muốn hiển thị hai tài nguyên, câu trả lời của James Westgate là điển hình hơn
Kathy Van Stone

Đây là câu trả lời chính xác. Trong tương lai gần, tôi chắc chắn chúng ta sẽ thấy một số bộ lọc = id trong (a, b, c, ...) được OData hỗ trợ hoặc đại loại như thế.
Bart Calix đến

Đây là cách Akka HTTP hoạt động
Joan

20

Đầu tiên:

Tôi nghĩ bạn có thể làm theo 2 cách

http://our.api.com/Product/<id> : nếu bạn chỉ muốn một bản ghi

http://our.api.com/Product : nếu bạn muốn tất cả các hồ sơ

http://our.api.com/Product/<id1>,<id2> : như James đề xuất có thể là một tùy chọn vì những gì xuất hiện sau thẻ Sản phẩm là một tham số

Hoặc cái tôi thích nhất là:

Bạn có thể sử dụng Hypermedia làm công cụ của thuộc tính trạng thái ứng dụng (HATEOAS) của RestFul WS và thực hiện một cuộc gọi http://our.api.com/Productsẽ trả về các url tương đương http://our.api.com/Product/<id>và gọi chúng sau này.

Thứ hai

Khi bạn phải thực hiện các truy vấn về các cuộc gọi url. Tôi sẽ đề nghị sử dụng HATEOAS một lần nữa.

1) Thực hiện cuộc gọi đến http://our.api.com/term/pumas/productType/clothing/color/black

2) Thực hiện cuộc gọi đến http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (Sử dụng HATEOAS) Thực hiện cuộc gọi tới ` http://our.api.com/term/pumas/productType/ -> nhận các url tất cả các url có thể có của quần áo -> gọi cho những người bạn muốn (quần áo và túi xách) - > nhận các url màu có thể -> gọi những cái bạn muốn


1
Tôi đã bị đặt vào tình huống tương tự vài ngày trước, Phải điều chỉnh api (HATEOAS) để lấy danh sách các đối tượng được lọc (lớn) và tôi chỉ chọn giải pháp thứ hai của bạn. Không nhớ lại api nhiều lần cho mỗi người một chút quá mức?
Samson

Nó thực sự phụ thuộc vào hệ thống của bạn .... Nếu nó là một đơn giản với một vài "tùy chọn" thì có lẽ nó sẽ là quá mức cần thiết. Tuy nhiên, nếu bạn có một số danh sách thực sự lớn, việc thực hiện tất cả trong một cuộc gọi lớn có thể trở nên thực sự rắc rối, ngoài ra nếu API của bạn được công khai, nó có thể trở nên phức tạp đối với người dùng (nếu là riêng tư thì sẽ dễ dàng hơn ... chỉ dạy cho người dùng mà bạn biết). Thay vào đó, bạn có thể triển khai cả hai kiểu, HATEOAS và một cuộc gọi "mảng không nghỉ" cho người dùng nâng cao
Diego Dias

Tôi đang xây dựng một dịch vụ web api đầy đủ trong đường ray và tôi cần tuân theo cấu trúc url giống như trên ( our.api.com/term/pumas/productType/cloth/color/black ). Nhưng tôi không chắc làm thế nào để cấu hình các tuyến đường cho phù hợp.
rubyist

hạn, ProductType và tô màu bộ điều khiển của bạn? Nếu vậy, bạn chỉ cần làm: tài nguyên: hạn làm tài nguyên: sản phẩm Loại tài nguyên: kết thúc màu
Diego Dias

ProductType và color là các thông số. Vì vậy, thông số của sản phẩm là quần áo và thông số của quần áo là màu đen
rubyist

12

Bạn có thể muốn kiểm tra RFC 6570 . Thông số mẫu URI này hiển thị nhiều ví dụ về cách các url có thể chứa tham số.


1
Mục 3.2.8 dường như là những gì được áp dụng. Mặc dù đáng lưu ý rằng đây chỉ là một tiêu chuẩn được đề xuất và dường như không vượt quá điểm đó.
Mike Post

3
@MikePost Bây giờ IETF đã chuyển sang quy trình trưởng thành hai bước cho các tài liệu "theo dõi tiêu chuẩn", tôi hy vọng 6570 sẽ giữ nguyên như thế này trong vài năm nữa trước khi chuyển sang "Tiêu chuẩn Internet". tools.ietf.org/html/rfc6410 Thông số kỹ thuật cực kỳ ổn định, có nhiều triển khai và được sử dụng rộng rãi.
Darrel Miller

Ah tôi đã không nhận thức được sự thay đổi đó. (Hoặc, TIL IETF bây giờ hợp lý hơn.) Cảm ơn!
Mike Post

8

Trường hợp đầu tiên:

Một tra cứu sản phẩm bình thường sẽ như thế này

http://our.api.com/product/1

Vì vậy, tôi nghĩ rằng thực hành tốt nhất sẽ là dành cho bạn để làm điều này

http://our.api.com/Product/101404,7267261

Trường hợp thứ hai

Tìm kiếm với các tham số chuỗi truy vấn - tốt như thế này. Tôi sẽ bị cám dỗ để kết hợp các thuật ngữ với AND và OR thay vì sử dụng [].

PS Điều này có thể chủ quan, vì vậy hãy làm những gì bạn cảm thấy thoải mái.

Lý do đưa dữ liệu vào url là để liên kết có thể được dán trên một trang web / được chia sẻ giữa những người dùng. Nếu đây không phải là một vấn đề, bằng mọi cách, hãy sử dụng JSON / POST thay thế.

EDIT: Theo phản ánh tôi nghĩ cách tiếp cận này phù hợp với một thực thể có khóa ghép, nhưng không phải là truy vấn cho nhiều thực thể.


3
Tất nhiên, trong cả hai ví dụ, dấu vết /không nên có vì URI xử lý tài nguyên, không phải là bộ sưu tập.
Lawrence Dol

2
Tôi luôn luôn mặc dù các động từ HTTP, trong cách sử dụng REST là thực hiện các hành động cụ thể và đây là dòng hướng dẫn: GET: lấy / đọc đối tượng, đối tượng tạo POST, PUT cập nhật đối tượng hiện có và XÓA xóa một đối tượng. Vì vậy, tôi sẽ không sử dụng POST để lấy thứ gì đó. Nếu tôi muốn có một danh sách cụ thể (bộ lọc), tôi sẽ thực hiện một GET với một danh sách các tham số url (được phân tách bằng dấu phẩy có vẻ tốt)
Alex

1

Tôi sẽ đứng về phía câu trả lời của chiến lược vì nó đã hoàn thành và dường như nó đã làm hài lòng nhu cầu của bạn. Mặc dù vậy, tôi muốn thêm một nhận xét về việc xác định nhiều (1 hoặc nhiều) tài nguyên theo cách đó:

http://our.api.com/Product/101404,7267261

Khi làm như vậy, bạn:

Làm phức tạp các khách hàng bằng cách buộc họ diễn giải phản hồi của bạn dưới dạng một mảng, điều này với tôi là phản biện trực quan nếu tôi thực hiện yêu cầu sau:http://our.api.com/Product/101404

Tạo các API dự phòng với một API để nhận tất cả các sản phẩm và một API ở trên để nhận 1 hoặc nhiều API. Vì bạn không nên hiển thị nhiều hơn 1 trang chi tiết cho người dùng vì UX, tôi tin rằng có nhiều hơn 1 ID sẽ vô dụng và hoàn toàn được sử dụng để lọc các sản phẩm.

Nó có thể không có vấn đề gì, nhưng bạn sẽ phải tự xử lý phía máy chủ này bằng cách trả về một thực thể duy nhất (bằng cách xác minh xem phản hồi của bạn có chứa một hoặc nhiều) hay để khách hàng quản lý nó.

Thí dụ

Tôi muốn đặt một cuốn sách từ Amazing . Tôi biết chính xác đó là cuốn sách nào và tôi thấy nó trong danh sách khi điều hướng cho các cuốn sách kinh dị:

  1. 10 000 dòng tuyệt vời, 0 thử nghiệm tuyệt vời
  2. Sự trở lại của quái vật đáng kinh ngạc
  3. Hãy nhân đôi mã tuyệt vời
  4. Sự khởi đầu tuyệt vời của sự kết thúc

Sau khi chọn cuốn sách thứ hai, tôi được chuyển hướng đến một trang mô tả chi tiết phần sách trong danh sách:

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

Hoặc trong một trang chỉ cung cấp cho tôi chi tiết đầy đủ của cuốn sách đó?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

Ý kiến ​​cá nhân của tôi

Tôi sẽ đề nghị sử dụng ID trong biến đường dẫn khi tính đơn nhất được đảm bảo khi nhận thông tin chi tiết về tài nguyên này. Ví dụ: các API bên dưới đề xuất nhiều cách để có được thông tin chi tiết cho một tài nguyên cụ thể (giả sử một sản phẩm có ID duy nhất và thông số kỹ thuật cho sản phẩm đó có một tên duy nhất và bạn có thể điều hướng từ trên xuống):

/products/{id}
/products/{id}/specs/{name}

Thời điểm bạn cần nhiều hơn 1 tài nguyên, tôi sẽ đề nghị lọc từ bộ sưu tập lớn hơn. Cho ví dụ tương tự:

/products?ids=

Tất nhiên, đây là ý kiến ​​của tôi vì nó không bị áp đặt.

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.