2015-02-19 26 views
42

Giả sử có hai tài nguyên BinderDoc với mối quan hệ liên kết có nghĩa là tự mình DocBinder. Doc có thể hoặc không thuộc về BinderBinder có thể trống.REST API - Tạo hoặc cập nhật hàng loạt trong một yêu cầu duy nhất

Nếu tôi muốn thiết kế một REST API cho phép người dùng gửi một tập hợp các Doc s, TRONG MỘT YÊU CẦU SINGLE, như sau:

{ 
    "docs": [ 
    {"doc_number": 1, "binder": 1}, 
    {"doc_number": 5, "binder": 8}, 
    {"doc_number": 6, "binder": 3} 
    ] 
} 

Và đối với từng tài liệu trong docs,

  • Nếu doc tồn tại sau đó gán nó vào Binder
  • Nếu doc nai không tồn tại, tạo nó và sau đó gán nó

Tôi thực sự bối rối về cách thực hiện điều này.

  • Phương thức HTTP nào sẽ sử dụng?
  • Mã phản hồi nào phải được trả lại?
  • Điều này thậm chí có đủ điều kiện cho REST không?
  • URI trông như thế nào? /binders/docs?
  • Xử lý yêu cầu hàng loạt, điều gì sẽ xảy ra nếu một vài mục tăng lỗi nhưng cách khác sẽ gặp phải. Mã phản hồi nào phải được trả lại? Hoạt động hàng loạt có nên là nguyên tử không?

Trả lời

25

Tôi nghĩ rằng bạn có thể sử dụng phương thức POST hoặc PATCH để xử lý việc này vì chúng thường thiết kế cho việc này.

  • Sử dụng một phương pháp POST thường được sử dụng để thêm một phần tử khi sử dụng tài nguyên trên danh sách nhưng bạn cũng có thể hỗ trợ một vài hành động cho phương pháp này. Xem câu trả lời này: How to Update a REST Resource Collection. Bạn cũng có thể hỗ trợ các định dạng biểu diễn khác nhau cho đầu vào (nếu chúng tương ứng với một mảng hoặc một phần tử đơn lẻ).

    Trong trường hợp đó, không cần phải xác định định dạng của bạn để mô tả bản cập nhật.

  • Sử dụng phương thức PATCH cũng phù hợp vì yêu cầu tương ứng tương ứng với cập nhật một phần. Theo RFC5789 (http://tools.ietf.org/html/rfc5789):

    Một số ứng dụng mở rộng Giao thức truyền siêu văn bản (HTTP) yêu cầu tính năng sửa đổi tài nguyên một phần. Phương thức HTTP PUT hiện tại chỉ cho phép thay thế hoàn toàn một tài liệu. Đề xuất này thêm phương thức HTTP mới, PATCH, để sửa đổi tài nguyên HTTP hiện có.

    Trong trường hợp này, bạn phải xác định định dạng của mình để mô tả cập nhật một phần.

Tôi nghĩ rằng trong trường hợp này, POSTPATCH khá tương tự kể từ khi bạn không thực sự cần để mô tả các hoạt động để làm cho mỗi phần tử. Tôi sẽ nói rằng nó phụ thuộc vào định dạng của biểu diễn để gửi.

Trường hợp PUT ít rõ ràng hơn một chút. Trong thực tế, khi sử dụng phương thức PUT, bạn nên cung cấp toàn bộ danh sách. Trên thực tế, đại diện được cung cấp trong yêu cầu sẽ thay thế cho tài nguyên danh sách.

Bạn có thể có hai tùy chọn liên quan đến đường dẫn tài nguyên.

  • Sử dụng con đường tài nguyên cho danh sách doc

Trong trường hợp này, bạn cần phải explicitely cung cấp liên kết tài liệu với một chất kết dính trong các đại diện mà bạn cung cấp trong yêu cầu.

Đây là tuyến đường mẫu cho /docs này.

Nội dung của phương pháp như vậy có thể cho phương pháp POST:

[ 
    { "doc_number": 1, "binder": 4, (other fields in the case of creation) }, 
    { "doc_number": 2, "binder": 4, (other fields in the case of creation) }, 
    { "doc_number": 3, "binder": 5, (other fields in the case of creation) }, 
    (...) 
] 
  • Sử dụng con đường nguồn phụ của nguyên tố kết dính

Ngoài ra bạn cũng có thể xem xét để tận dụng các tuyến đường phụ để mô tả liên kết giữa các tài liệu và các chất kết dính. Các gợi ý liên quan đến sự liên kết giữa một tài liệu và một chất kết dính không được xác định trong nội dung yêu cầu.

Đây là tuyến đường mẫu cho số /binder/{binderId}/docs này. Trong trường hợp này, việc gửi danh sách tài liệu có phương thức POST hoặc PATCH sẽ đính kèm tài liệu vào bộ nhận dạng có mã định danh binderId sau khi đã tạo tài liệu nếu tài liệu không tồn tại.

Nội dung của phương pháp như vậy có thể cho phương pháp POST:

[ 
    { "doc_number": 1, (other fields in the case of creation) }, 
    { "doc_number": 2, (other fields in the case of creation) }, 
    { "doc_number": 3, (other fields in the case of creation) }, 
    (...) 
] 

Về phản ứng, đó là tùy thuộc vào bạn để xác định mức độ phản ứng và các lỗi trở lại. Tôi thấy hai cấp độ: mức trạng thái (mức toàn cầu) và mức tải trọng (mức độ mỏng hơn). Nó cũng tùy thuộc vào bạn để xác định xem tất cả các chèn/cập nhật tương ứng với yêu cầu của bạn phải là nguyên tử hay không.

  • Atomic

Trong trường hợp này, bạn có thể tận dụng các trạng thái HTTP. Nếu mọi thứ suôn sẻ, bạn sẽ nhận được trạng thái 200. Nếu không, một trạng thái khác như 400 nếu dữ liệu được cung cấp không chính xác (ví dụ: id binder không hợp lệ) hoặc cái gì khác.

  • Non nguyên tử

Trong trường hợp này, một tình trạng 200 sẽ được trả lại và nó tùy thuộc vào các đại diện phản ứng để mô tả những gì đã được thực hiện và nơi lỗi cuối cùng xảy ra. ElasticSearch có một điểm cuối trong API REST của nó để cập nhật hàng loạt. Điều này có thể cung cấp cho bạn một số ý tưởng ở cấp độ này: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html.

  • Asynchronous

Bạn cũng có thể thực hiện một xử lý không đồng bộ để xử lý các dữ liệu được cung cấp. Trong trường hợp này, trả về trạng thái HTTP sẽ là 202. Khách hàng cần phải kéo thêm tài nguyên để xem điều gì xảy ra.

Trước khi kết thúc, tôi cũng muốn lưu ý rằng đặc tả OData giải quyết vấn đề liên quan đến quan hệ giữa các thực thể với tính năng có tên liên kết điều hướng. Có lẽ bạn có thể xem cái này ;-)

Liên kết sau cũng có thể giúp bạn: https://templth.wordpress.com/2014/12/15/designing-a-web-api/.

Hy vọng nó giúp bạn, Thierry

10

PUT ing

PUT /binders/{id}/docs Tạo hoặc cập nhật, và liên hệ một tài liệu duy nhất để một chất kết dính

ví dụ .:

PUT /binders/1/docs HTTP/1.1 
{ 
    "docNumber" : 1 
} 

PATCH ing

PATCH /docs Tạo tài liệu nếu họ không tồn tại và gắn chúng với chất kết dính

ví dụ:

PATCH /docs HTTP/1.1 
[ 
    { "op" : "add", "path" : "/binder/1/docs", "value" : { "doc_number" : 1 } }, 
    { "op" : "add", "path" : "/binder/8/docs", "value" : { "doc_number" : 8 } }, 
    { "op" : "add", "path" : "/binder/3/docs", "value" : { "doc_number" : 6 } } 
] 

tôi sẽ bao gồm những hiểu biết thêm sau, nhưng trong khi chờ đợi nếu bạn muốn, có một cái nhìn tại RFC 5789, RFC 6902 và mục nhập blog Please. Don't Patch Like an Idiot của William Durand.

+1

Đôi khi khách hàng cần hoạt động hàng loạt và không muốn quan tâm liệu tài nguyên có ở đó hay không. Như tôi đã nói trong câu hỏi, khách hàng muốn gửi một loạt các "tài liệu" và liên kết chúng với 'binders'. Khách hàng muốn tạo các liên kết nếu họ không tồn tại và tạo liên kết nếu họ làm. Trong MỘT yêu cầu BULK SINGLE. – norbertpy

18

Bạn có thể sẽ cần sử dụng POST hoặc PATCH, vì không chắc rằng một yêu cầu duy nhất cập nhật và tạo nhiều tài nguyên sẽ không có giá trị.

Làm PATCH /docs chắc chắn là một tùy chọn hợp lệ. Bạn có thể tìm thấy bằng cách sử dụng các định dạng vá tiêu chuẩn khó khăn cho kịch bản cụ thể của bạn. Không chắc chắn về điều này.

Bạn có thể sử dụng 200.Bạn cũng có thể sử dụng 207 - Multi Status

Điều này có thể được thực hiện theo cách RESTful. Chìa khóa, theo ý kiến ​​của tôi, là có một số tài nguyên được thiết kế để chấp nhận một bộ tài liệu để cập nhật/tạo.

Nếu bạn sử dụng phương pháp PATCH, tôi cho rằng hoạt động của bạn phải là nguyên tử. tức là tôi sẽ không sử dụng mã trạng thái 207 và sau đó báo cáo thành công và thất bại trong nội dung phản hồi. Nếu bạn sử dụng phép toán POST thì phương thức 207 là khả thi. Bạn sẽ phải thiết kế cơ thể phản hồi của riêng bạn để giao tiếp những hoạt động nào đã thành công và thất bại. Tôi không biết về tiêu chuẩn hóa.

+0

Cảm ơn bạn rất nhiều. Bởi 'Điều này có thể được thực hiện trong một cách RESTful' bạn có nghĩa là Cập nhật và Tạo phải được thực hiện riêng biệt? – norbertpy

+0

@norbertpy Thực hiện một số thao tác ghi trên tài nguyên có thể khiến các tài nguyên khác được cập nhật và tạo ra từ một yêu cầu duy nhất. REST không có vấn đề gì với điều đó. Lựa chọn cụm từ của tôi là vì một số khung công tác thực hiện các hoạt động hàng loạt bằng cách tuần tự hóa các yêu cầu HTTP thành các tài liệu đa phần và sau đó gửi các yêu cầu HTTP được tuần tự hóa thành một lô. Tôi nghĩ rằng cách tiếp cận vi phạm ràng buộc REST xác định tài nguyên. –

1

Trong một dự án tôi làm việc tại chúng tôi giải quyết vấn đề này bằng cách thực hiện một cái gì đó chúng ta gọi là 'hàng loạt' yêu cầu. Chúng tôi xác định một con đường /batch nơi chúng tôi chấp nhận json theo định dạng sau:

[ 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     doc_number: 1, 
     binder: 1 
     } 
    }, 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     doc_number: 5, 
     binder: 8 
     } 
    }, 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     doc_number: 6, 
     binder: 3 
     } 
    }, 
] 

Câu trả lời có mã trạng thái 207 (Multi-Status) và trông như thế này:

[ 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     doc_number: 1, 
     binder: 1 
     } 
     status: 200 
    }, 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     error: { 
      msg: 'A document with doc_number 5 already exists' 
      ... 
     } 
     }, 
     status: 409 
    }, 
    { 
     path: '/docs', 
     method: 'post', 
     body: { 
     doc_number: 6, 
     binder: 3 
     }, 
     status: 200 
    }, 
] 

Bạn cũng có thể thêm hỗ trợ cho tiêu đề trong cấu trúc này. Chúng tôi đã triển khai một cái gì đó đã chứng tỏ hữu ích đó là các biến để sử dụng giữa các yêu cầu trong một đợt, nghĩa là chúng tôi có thể sử dụng phản hồi từ một yêu cầu làm đầu vào cho một yêu cầu khác.

Facebook và Google đã triển khai tương tự:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

Khi bạn muốn tạo hoặc cập nhật tài nguyên với các cuộc gọi cùng tôi sẽ sử dụng POST hoặc PUT tùy từng trường hợp. Nếu tài liệu đã tồn tại, bạn có muốn toàn bộ tài liệu là:

  1. Thay thế bằng tài liệu bạn gửi (tức là các thuộc tính bị thiếu trong yêu cầu sẽ bị xóa và đã ghi đè)?
  2. Hợp nhất với tài liệu bạn gửi (nghĩa là các thuộc tính bị thiếu trong yêu cầu sẽ không bị xóa và các thuộc tính hiện có sẽ bị ghi đè)?

Trong trường hợp bạn muốn hành vi thay thế 1, bạn nên sử dụng POST và trong trường hợp bạn muốn hành vi thay thế 2, bạn nên sử dụng PUT.

http://restcookbook.com/HTTP%20Methods/put-vs-post/

Khi mọi người đã gợi ý bạn cũng có thể đi cho PATCH, nhưng tôi muốn giữ API đơn giản và không sử dụng các động từ thêm nếu họ không cần thiết.

+3

Giống như câu trả lời này cho Proof-of-Concept cũng như các liên kết của Google và Facebook. Nhưng không đồng ý với phần kết thúc về POST hoặc PUT. Trong 2 trường hợp, câu trả lời được đề cập, câu hỏi đầu tiên phải là PUT và câu thứ hai nên là PATCH. – RayLuo

+0

@RayLuo, bạn có thể giải thích tại sao chúng ta cần PATCH ngoài POST và PUT không? –

+1

Vì đó là những gì PATCH được phát minh. Bạn có thể đọc [định nghĩa này] (https://tools.ietf.org/html/rfc5789#section-2) và xem cách PUT và PATCH khớp với 2 dấu đầu dòng của bạn. – RayLuo

Các vấn đề liên quan