REST Complex / Composite / Nested resource [đã đóng]


177

Tôi đang cố gắng xoay quanh cách tốt nhất để giải quyết các khái niệm trong API dựa trên REST. Tài nguyên phẳng không chứa các tài nguyên khác không có vấn đề. Nơi tôi đang gặp rắc rối là các tài nguyên phức tạp.

Ví dụ, tôi có một tài nguyên cho một cuốn truyện tranh. ComicBookcó tất cả các loại tài sản trên nó thích author, issue number, datevv

Một cuốn truyện tranh cũng có một danh sách các 1..nbìa. Những bìa này là những đối tượng phức tạp. Chúng chứa rất nhiều thông tin về trang bìa: nghệ sĩ, một ngày, và thậm chí là một hình ảnh 64 được mã hóa cơ sở của trang bìa.

Đối với một GETtrên ComicBooktôi chỉ có thể trả lại truyện tranh, và tất cả các trang bìa bao gồm cả hình ảnh cơ sở 64 của họ. Đó có lẽ không phải là một vấn đề lớn để có được một truyện tranh. Nhưng giả sử tôi đang xây dựng một ứng dụng khách muốn liệt kê tất cả truyện tranh trong hệ thống trong một bảng.
Bảng sẽ chứa một vài thuộc tính từ ComicBooktài nguyên, nhưng chúng tôi chắc chắn sẽ không muốn hiển thị tất cả các bìa trong bảng. Trả lại 1000 cuốn truyện tranh, mỗi cuốn có nhiều bìa sẽ dẫn đến một lượng dữ liệu lớn đến mức lố bịch, dữ liệu không cần thiết cho người dùng cuối trong trường hợp đó.

Bản năng của tôi là tạo ra Covermột tài nguyên và có ComicBookchứa bìa. Vì vậy, bây giờ Coverlà một URI. GETbây giờ trên truyện tranh hoạt động, thay vì Covertài nguyên khổng lồ, chúng tôi gửi lại URI cho mỗi trang bìa và khách hàng có thể truy xuất tài nguyên Cover khi họ yêu cầu.

Bây giờ tôi có một vấn đề với việc tạo ra truyện tranh mới. Chắc chắn tôi sẽ muốn tạo ra ít nhất một trang bìa khi tôi tạo Comic, thực tế đó có thể là một quy tắc kinh doanh.
Vì vậy, bây giờ tôi đang gặp khó khăn, tôi hoặc là buộc các khách hàng để thực thi quy tắc kinh doanh bằng cách đầu tiên gửi một Cover, nhận được URI cho trang bìa, sau đó POSTing một ComicBookvới URI trong danh sách, hoặc tôi POSTtrên ComicBookmất trong một nguồn tìm kiếm khác với nó spits ngoài. Các tài nguyên đến POSTGETlà các bản sao sâu, trong đó các GETtài liệu gửi đi chứa các tham chiếu đến các tài nguyên phụ thuộc.

Tài Covernguyên có thể là cần thiết trong mọi trường hợp bởi vì tôi chắc chắn là khách hàng tôi muốn giải quyết hướng đi trong một số trường hợp. Vì vậy, vấn đề tồn tại ở dạng chung bất kể quy mô của tài nguyên phụ thuộc. Nói chung, làm thế nào để bạn xử lý các tài nguyên phức tạp mà không buộc khách hàng chỉ "biết" cách các tài nguyên đó được tạo ra như thế nào?


sử dụng KHÁM PHÁ DỊCH VỤ RESTFUL có ý nghĩa gì không?
treecoder

1
Tôi đang cố gắng tuân thủ HATEAOS, theo suy nghĩ của tôi, nó chống lại việc sử dụng một cái gì đó như thế nhưng tôi sẽ xem xét.
jgerman

Câu hỏi khác nhau trong cùng một tinh thần. Tuy nhiên, quyền sở hữu khác với giải pháp đề xuất của bạn (Người trong câu hỏi). stackoverflow.com/questions/20951419/ Mạnh
Wes

Câu trả lời:


64

@ray, thảo luận tuyệt vời

@jgerman, đừng quên rằng chỉ vì đó là REST, không có nghĩa là tài nguyên phải được đặt trong POST từ POST.

Những gì bạn chọn để đưa vào bất kỳ đại diện nhất định nào của tài nguyên là tùy thuộc vào bạn.

Trường hợp của bạn về các trang bìa được tham chiếu riêng chỉ là việc tạo ra một tài nguyên gốc (truyện tranh) có tài nguyên con (bìa) có thể được tham chiếu chéo. Ví dụ: bạn cũng có thể muốn cung cấp tài liệu tham khảo cho các tác giả, nhà xuất bản, nhân vật hoặc danh mục riêng biệt. Bạn có thể muốn tạo các tài nguyên này một cách riêng biệt hoặc trước cuốn truyện tranh tham chiếu chúng như tài nguyên con. Ngoài ra, bạn có thể muốn tạo tài nguyên con mới khi tạo tài nguyên gốc.

Trường hợp cụ thể của bạn về bìa phức tạp hơn một chút ở chỗ bìa thực sự đòi hỏi phải có truyện tranh và ngược lại.

Tuy nhiên, nếu bạn coi thư email là tài nguyên và địa chỉ từ là tài nguyên con, rõ ràng bạn vẫn có thể tham chiếu địa chỉ từ địa chỉ riêng. Ví dụ, lấy tất cả từ địa chỉ. Hoặc, tạo một tin nhắn mới với một địa chỉ trước đó từ địa chỉ. Nếu email là REST, bạn có thể dễ dàng thấy rằng nhiều tài nguyên được tham chiếu chéo có thể có sẵn: / tin nhắn đã nhận, / thư nháp, / từ địa chỉ, / đến địa chỉ, / địa chỉ, / chủ đề, / tệp đính kèm, / thư mục , / tags, / chủng loại, / nhãn, et al.

Hướng dẫn này cung cấp một ví dụ tuyệt vời về tài nguyên tham chiếu chéo. http://www.peej.co.uk/articles/restfully-delicy.html

Đây là mẫu phổ biến nhất cho dữ liệu được tạo tự động. Ví dụ: bạn không đăng URI, ID hoặc ngày tạo cho tài nguyên mới, vì chúng được tạo bởi máy chủ. Tuy nhiên, bạn có thể truy xuất URI, ID hoặc ngày tạo khi bạn lấy lại tài nguyên mới.

Một ví dụ trong trường hợp dữ liệu nhị phân của bạn. Ví dụ: bạn muốn đăng dữ liệu nhị phân dưới dạng tài nguyên con. Khi bạn nhận được tài nguyên gốc, bạn có thể biểu diễn các tài nguyên con đó dưới dạng cùng một dữ liệu nhị phân hoặc dưới dạng URI đại diện cho dữ liệu nhị phân.

Các biểu mẫu & tham số đã khác với các biểu diễn HTML của tài nguyên. Đăng một tham số nhị phân / tệp dẫn đến URL không phải là một sự kéo dài.

Khi bạn nhận được biểu mẫu cho tài nguyên mới (/ truyện tranh / mới) hoặc lấy biểu mẫu để chỉnh sửa tài nguyên (/ truyện tranh / 0 / chỉnh sửa), bạn sẽ yêu cầu đại diện cho tài nguyên theo mẫu cụ thể. Nếu bạn đăng nó lên bộ sưu tập tài nguyên với kiểu "application / x-www-form-urlencoding" hoặc "multiart / form-data", bạn sẽ yêu cầu máy chủ lưu biểu diễn kiểu đó. Máy chủ có thể phản hồi với biểu diễn HTML đã được lưu hoặc bất cứ điều gì.

Bạn cũng có thể muốn cho phép một sự hồi phục HTML, XML hoặc JSON được đăng lên bộ sưu tập tài nguyên, cho các mục đích của API hoặc tương tự.

Cũng có thể đại diện cho tài nguyên và quy trình làm việc của bạn như bạn mô tả, có tính đến các bìa tài khoản được đăng sau truyện tranh, nhưng yêu cầu truyện tranh phải có bìa. Ví dụ như sau.

  • Cho phép tạo bìa chậm trễ
  • Cho phép tạo truyện tranh với bìa yêu cầu
  • Cho phép bìa được tham chiếu chéo
  • Cho phép nhiều bìa
  • Tạo bản thảo truyện tranh
  • Tạo bìa truyện tranh
  • Xuất bản dự thảo truyện tranh

NHẬN / truyện tranh
=> 200 OK, Nhận tất cả truyện tranh.

NHẬN / truyện tranh / 0
=> 200 OK, Nhận truyện tranh (id: 0) với bìa (/ bìa / 1, / bìa / 2).

NHẬN / truyện tranh / 0 / bìa
=> 200 OK, Nhận bìa cho truyện tranh (id: 0).

NHẬN / bìa
=> 200 OK, Nhận tất cả các bìa.

NHẬN / bìa / 1
=> 200 OK, Nhận bìa (id: 1) với truyện tranh (/ truyện tranh / 0).

NHẬN / truyện tranh / mới
=> 200 OK, Nhận mẫu để tạo truyện tranh (mẫu: POST / bản nháp-truyện tranh).

POST / nháp-truyện tranh-
tiêu đề = foo
tác giả = boo
nhà xuất bản = goo
xuất bản = 2011-01-01
=> 302 Đã tìm thấy, Địa điểm: / dự thảo truyện tranh / 3, Chuyển hướng đến dự thảo truyện tranh (id: 3) với bìa (nhị phân).

NHẬN / bản nháp-truyện tranh / 3
=> 200 OK, Nhận bản nháp truyện tranh (id: 3) có bìa.

NHẬN / bản nháp-truyện tranh / 3 / bìa
=> 200 OK, Nhận bìa cho bản nháp truyện tranh (/ bản nháp-truyện tranh / 3).

NHẬN / bản nháp-truyện tranh / 3 / bìa / mới
=> 200 OK, Nhận biểu mẫu để tạo bìa cho bản thảo truyện tranh (/ bản nháp-truyện tranh / 3) (mẫu: POST / bản nháp-truyện tranh / 3 / bao gồm).

POST / nháp-truyện tranh-3 / bìa
cover_type = front
cover_data = (binary)
=> 302 Đã tìm thấy, Địa điểm: / nháp-truyện tranh / 3 / bìa, Chuyển hướng sang bìa mới cho truyện tranh nháp (/ nháp-truyện tranh -book / 3 / bìa / 1).

NHẬN / bản nháp-truyện tranh / 3 / xuất bản
=> 200 OK, Nhận mẫu để xuất bản bản thảo truyện tranh (id: 3) (mẫu: POST / xuất bản-truyện tranh).

POST / đã xuất bản truyện tranh
tiêu đề = foo
tác giả = boo
nhà xuất bản = goo
xuất bản = 2011-01-01
cover_type = front
cover_data = (binary)
=> 302 Tìm thấy, Vị trí: / truyện tranh / 3, Chuyển hướng đến truyện tranh đã xuất bản (id: 3) có bìa.


Tôi là một người hoàn toàn mới với điều này và đang cố gắng học nó một cách vội vàng. Tôi thấy điều này cực kỳ hữu ích. Tuy nhiên, trong các blog khác, v.v. Tôi đã đọc ngày hôm nay, việc sử dụng GET để thực hiện một thao tác (đặc biệt là một hoạt động không bình thường) sẽ được tán thành. Vì vậy, nó không nên là POST / nháp-truyện tranh / 3 / xuất bản?
Gary McGill

3
@GaryMcGill Trong ví dụ của mình, / nháp-truyện tranh / 3 / xuất bản chỉ trả về một biểu mẫu HTML (không sửa đổi bất kỳ dữ liệu nào).
Olivier Lalonde

@Olivier là chính xác. Từ xuất bản là ở đó để biểu thị những gì hình thức làm. Tuy nhiên, vì bạn muốn giữ các động từ giới hạn trong các phương thức HTTP, bạn nên đăng lên một tài nguyên cho các truyện tranh được xuất bản. ... Nếu đây là một trang web, bạn có thể cần một URI cho biểu mẫu để xuất bản một cái gì đó. ... Mặc dù, nếu hành động xuất bản chỉ là một nút duy nhất trên trang truyện tranh, thì hình thức một nút đó có thể đăng trực tiếp lên URI / xuất bản truyện tranh.
Alex

@Alex, trong yêu cầu POST, thay vào đó tôi sẽ trả về 201 được tạo, với URL của tài nguyên mới là Vị trí trong tiêu đề phản hồi.
ismriv 17/12/13

2
@Stephane, chuyển hướng chỉ làm cho mọi thứ đơn giản hơn cho các bộ điều khiển. Ngay cả đối với API, đơn giản hơn là bộ điều khiển tạo trả về vị trí cho nội dung mới và sau đó để bộ điều khiển hiển thị xử lý việc hiển thị nội dung mới. Mặc dù, ứng dụng khách API sẽ đơn giản hơn / đơn giản hơn để có được nội dung và không bận tâm với các chuyển hướng.
Alex

45

Xử lý các trang bìa như tài nguyên chắc chắn là theo tinh thần của REST, đặc biệt là HATEOAS. Vì vậy, có, một GETyêu cầu http://example.com/comic-books/1sẽ cung cấp cho bạn một đại diện của cuốn sách 1, với các thuộc tính bao gồm một bộ URI cho bìa. Càng xa càng tốt.

Câu hỏi của bạn là làm thế nào để đối phó với sáng tạo truyện tranh. Nếu quy tắc kinh doanh của bạn là một cuốn sách sẽ có 0 hoặc nhiều bìa, thì bạn không có vấn đề gì:

POST http://example.com/comic-books

với dữ liệu truyện tranh không có bìa sẽ tạo ra một cuốn truyện tranh mới và trả về id máy chủ được tạo (giả sử nó trở lại là 8), và bây giờ bạn có thể thêm bìa vào như vậy:

POST http://example.com/comic-books/8/covers

với vỏ bọc trong cơ thể thực thể.

Bây giờ bạn có một câu hỏi hay đó là điều gì sẽ xảy ra nếu quy tắc kinh doanh của bạn nói rằng luôn phải có ít nhất một trang bìa. Dưới đây là một số lựa chọn, đầu tiên bạn xác định trong câu hỏi của mình:

  1. Buộc tạo bìa trước, bây giờ về cơ bản là tạo tài nguyên không phụ thuộc hoặc bạn đặt bìa ban đầu trong cơ thể thực thể của POST tạo ra truyện tranh. Điều này như bạn nói có nghĩa là đại diện bạn POST để tạo sẽ khác với đại diện bạn NHẬN.

  2. Xác định khái niệm bìa chính, hoặc ban đầu, hoặc ưu tiên, hoặc được chỉ định khác. Đây có thể là một hack mô hình hóa, và nếu bạn đã làm điều đó sẽ giống như điều chỉnh mô hình đối tượng của bạn (mô hình khái niệm hoặc mô hình kinh doanh của bạn) để phù hợp với công nghệ. Không phải là một ý tưởng tuyệt vời.

Bạn nên cân nhắc hai lựa chọn này với việc đơn giản là cho phép truyện tranh không có bìa.

Bạn nên chọn lựa nào trong ba lựa chọn? Không biết quá nhiều về tình huống của bạn, nhưng trả lời câu hỏi chung về tài nguyên phụ thuộc 1..N, tôi sẽ nói:

  • Nếu bạn có thể đi với 0..N cho lớp dịch vụ RESTful của mình, thật tuyệt. Có lẽ một lớp giữa RESTful SOA của bạn có thể xử lý các ràng buộc kinh doanh hơn nữa nếu cần ít nhất một lớp. (Không chắc nó trông như thế nào nhưng có thể đáng để khám phá .... người dùng cuối thường không nhìn thấy SOA.)

  • Nếu bạn chỉ đơn giản là phải mô hình hóa một ràng buộc 1..N, thì hãy tự hỏi liệu bìa có thể là tài nguyên có thể chia sẻ được hay không, nói cách khác, chúng có thể tồn tại trên những thứ khác ngoài truyện tranh. Bây giờ chúng không phải là tài nguyên phụ thuộc và bạn có thể tạo chúng trước và cung cấp URI trong POST của bạn để tạo truyện tranh.

  • Nếu bạn cần 1..N và các trang bìa vẫn phụ thuộc, chỉ cần thư giãn bản năng của bạn để giữ các biểu diễn trong POST và GET giống nhau, hoặc làm cho chúng giống nhau.

Mục cuối cùng được giải thích như vậy:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

Khi bạn POST, bạn cho phép uris hiện có nếu bạn có chúng (mượn từ những cuốn sách khác) nhưng cũng đưa vào một hoặc nhiều hình ảnh ban đầu. Nếu bạn đang tạo một cuốn sách và thực thể của bạn không có ảnh bìa ban đầu, hãy trả về 409 hoặc phản hồi tương tự. Trên GET, bạn có thể trả về URI ..

Vì vậy, về cơ bản, bạn đang cho phép các đại diện POST và GET "giống nhau" nhưng bạn chỉ chọn không "sử dụng" ảnh bìa trên GET cũng như không che trên POST. Hy vọng rằng có ý nghĩa.

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.