REST API - สร้างหรืออัปเดตจำนวนมากในคำขอเดียว [ปิด]


94

สมมติว่ามีทรัพยากรสองอย่างBinderและDocด้วยความสัมพันธ์ที่เชื่อมโยงกันหมายความว่าDocและBinderยืนอยู่ได้ด้วยตัวเอง Docอาจเป็นหรือไม่เป็นของBinderและBinderอาจว่างเปล่า

ถ้าฉันต้องการออกแบบ REST API ที่อนุญาตให้ผู้ใช้ส่งคอลเลกชันของDocs ในคำขอเดียวดังต่อไปนี้:

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

และสำหรับแต่ละ doc ในdocs,

  • หากdocมีอยู่ให้กำหนดให้Binder
  • หากdocไม่มีอยู่ให้สร้างแล้วกำหนด

ฉันสับสนจริงๆว่าควรจะนำไปใช้อย่างไร:

  • ใช้วิธี HTTP อะไร
  • ต้องส่งคืนรหัสตอบกลับอะไร
  • นี่เป็นคุณสมบัติสำหรับ REST หรือไม่?
  • URI มีลักษณะอย่างไร /binders/docsเหรอ?
  • การจัดการคำขอจำนวนมากจะเกิดอะไรขึ้นหากบางรายการเกิดข้อผิดพลาด แต่อีกรายการผ่านไป ต้องส่งคืนรหัสตอบกลับอะไร การดำเนินการจำนวนมากควรเป็นปรมาณูหรือไม่?

คำตอบ:


59

ฉันคิดว่าคุณสามารถใช้วิธี POST หรือ PATCH เพื่อจัดการสิ่งนี้ได้เนื่องจากโดยทั่วไปแล้วพวกเขาออกแบบมาสำหรับสิ่งนี้

  • โดยทั่วไปการใช้POSTเมธอดจะใช้เพื่อเพิ่มองค์ประกอบเมื่อใช้ในรายการทรัพยากร แต่คุณยังสามารถรองรับการดำเนินการต่างๆสำหรับวิธีนี้ได้ ดูคำตอบนี้: วิธีการอัพเดทคอลเลกชันทรัพยากร REST คุณยังสามารถรองรับรูปแบบการแทนค่าต่างๆสำหรับอินพุตได้ (หากสอดคล้องกับอาร์เรย์หรือองค์ประกอบเดียว)

    ในกรณีนี้คุณไม่จำเป็นต้องกำหนดรูปแบบของคุณเพื่ออธิบายการอัปเดต

  • การใช้PATCHเมธอดก็เหมาะสมเช่นกันเนื่องจากคำขอที่เกี่ยวข้องสอดคล้องกับการอัปเดตบางส่วน อ้างอิงจาก RFC5789 ( http://tools.ietf.org/html/rfc5789 ):

    แอปพลิเคชันหลายตัวที่ขยาย Hypertext Transfer Protocol (HTTP) ต้องการคุณสมบัติในการแก้ไขทรัพยากรบางส่วน เมธอด HTTP PUT ที่มีอยู่อนุญาตให้เปลี่ยนเอกสารทั้งหมดเท่านั้น ข้อเสนอนี้เพิ่มวิธี HTTP ใหม่ PATCH เพื่อแก้ไขทรัพยากร HTTP ที่มีอยู่

    ในกรณีนี้คุณต้องกำหนดรูปแบบของคุณเพื่ออธิบายการอัปเดตบางส่วน

ฉันคิดว่าในกรณีนี้POSTและPATCHค่อนข้างคล้ายกันเนื่องจากคุณไม่จำเป็นต้องอธิบายการดำเนินการสำหรับแต่ละองค์ประกอบ ฉันจะบอกว่ามันขึ้นอยู่กับรูปแบบของการเป็นตัวแทนที่จะส่ง

กรณีของPUTมีความชัดเจนน้อยกว่าเล็กน้อย ในความเป็นจริงเมื่อใช้วิธีPUTคุณควรระบุรายการทั้งหมด ตามความเป็นจริงการเป็นตัวแทนที่ให้ไว้ในคำขอจะแทนที่ทรัพยากรรายการหนึ่ง

คุณสามารถมีสองตัวเลือกเกี่ยวกับเส้นทางทรัพยากร

  • ใช้เส้นทางทรัพยากรสำหรับรายการเอกสาร

ในกรณีนี้คุณต้องระบุลิงก์ของเอกสารอย่างชัดเจนพร้อมกับตัวประสานในการแสดงที่คุณระบุในคำขอ

/docsนี่คือตัวอย่างสำหรับเส้นทางนี้

เนื้อหาของแนวทางดังกล่าวอาจมีไว้สำหรับวิธีการ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) },
    (...)
]
  • ใช้เส้นทางทรัพยากรย่อยขององค์ประกอบสารยึดเกาะ

นอกจากนี้คุณยังสามารถพิจารณาใช้ประโยชน์จากเส้นทางย่อยเพื่ออธิบายความเชื่อมโยงระหว่างเอกสารและตัวประสาน คำแนะนำเกี่ยวกับการเชื่อมโยงระหว่าง doc และ binder ไม่จำเป็นต้องระบุในเนื้อหาคำขอ

/binder/{binderId}/docsนี่คือตัวอย่างสำหรับเส้นทางนี้ ในกรณีนี้การส่งรายการเอกสารด้วยวิธีการPOSTหรือPATCHจะแนบเอกสารไปยังเครื่องผูกพร้อมตัวระบุbinderIdหลังจากสร้างเอกสารแล้วหากไม่มีอยู่

เนื้อหาของแนวทางดังกล่าวอาจมีไว้สำหรับวิธีการ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) },
    (...)
]

เกี่ยวกับการตอบกลับขึ้นอยู่กับคุณที่จะกำหนดระดับของการตอบกลับและข้อผิดพลาดที่จะส่งกลับ ฉันเห็นสองระดับ: ระดับสถานะ (ระดับโลก) และระดับน้ำหนักบรรทุก (ระดับทินเนอร์) นอกจากนี้ยังขึ้นอยู่กับคุณที่จะกำหนดว่าส่วนแทรก / การอัปเดตทั้งหมดที่เกี่ยวข้องกับคำขอของคุณต้องเป็นแบบอะตอมหรือไม่

  • ปรมาณู

ในกรณีนี้คุณสามารถใช้ประโยชน์จากสถานะ HTTP 200หากทุกอย่างเป็นไปด้วยดีคุณจะได้รับสถานะ ไม่เช่นนั้นสถานะอื่นเช่น400หากข้อมูลที่ให้มาไม่ถูกต้อง (เช่น binder id ไม่ถูกต้อง) หรืออย่างอื่น

  • ไม่ใช่ปรมาณู

ในกรณีนี้สถานะ200จะถูกส่งกลับและขึ้นอยู่กับการแสดงการตอบสนองเพื่ออธิบายสิ่งที่ทำและข้อผิดพลาดเกิดขึ้น ElasticSearch มีจุดสิ้นสุดใน REST API สำหรับการอัปเดตจำนวนมาก ซึ่งอาจทำให้คุณมีความคิดบางอย่างในระดับนี้: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/bulk.html

  • อะซิงโครนัส

คุณยังสามารถใช้การประมวลผลแบบอะซิงโครนัสเพื่อจัดการกับข้อมูลที่ให้มา ในกรณีนี้ผลตอบแทนสถานะ HTTP 202จะ ลูกค้าต้องดึงทรัพยากรเพิ่มเติมเพื่อดูว่าเกิดอะไรขึ้น

ก่อนที่จะจบผมยังต้องการที่จะแจ้งให้ทราบว่าสเปคที่อยู่ OData ปัญหาเกี่ยวกับความสัมพันธ์ระหว่างหน่วยงานที่มีคุณลักษณะชื่อลิงค์การนำทาง บางทีคุณสามารถดูสิ่งนี้ ;-)

ลิงค์ต่อไปนี้ยังสามารถช่วยให้คุณ: https://templth.wordpress.com/2014/12/15/designing-a-web-api/

หวังว่ามันจะช่วยคุณ Thierry


ฉันได้ติดตามคำถาม ฉันเลือกใช้เส้นทางแบบเรียบโดยไม่มีทรัพยากรย่อยซ้อนกัน ที่จะได้รับเอกสารทั้งหมดที่ผมเรียกและเรียกเอกสารทั้งหมดภายในเครื่องผูกโดยเฉพาะอย่างยิ่งGET /docs GET /docs?binder_id=xหากต้องการลบส่วนย่อยของทรัพยากรฉันจะเรียกใช้DELETE /docs?binder_id=xหรือฉันควรเรียกDELETE /docsใช้{"binder_id": x}ในเนื้อหาคำขอ คุณเคยใช้PATCH /docs?binder_id=xสำหรับการอัปเดตแบตช์หรือเพียงแค่PATCH /docsและผ่านคู่?
Andy Fusniak

35

คุณอาจจำเป็นต้องใช้ POST หรือ PATCH เนื่องจากไม่น่าเป็นไปได้ที่คำขอเดียวที่อัปเดตและสร้างทรัพยากรหลายรายการจะไม่เป็นไปตามข้อกำหนด

การPATCH /docsทำเป็นตัวเลือกที่ถูกต้องแน่นอน คุณอาจพบว่าการใช้รูปแบบโปรแกรมแก้ไขมาตรฐานนั้นยุ่งยากสำหรับสถานการณ์เฉพาะของคุณ ไม่แน่ใจเกี่ยวกับเรื่องนี้

คุณสามารถใช้ 200 คุณยังสามารถใช้207 - หลายสถานะ

สิ่งนี้สามารถทำได้ในลักษณะที่น่ารังเกียจ สิ่งสำคัญในความคิดของฉันคือการมีทรัพยากรบางอย่างที่ออกแบบมาเพื่อยอมรับชุดเอกสารที่จะอัปเดต / สร้าง

ถ้าคุณใช้วิธี PATCH ฉันคิดว่าการทำงานของคุณควรเป็นแบบปรมาณู กล่าวคือฉันจะไม่ใช้รหัสสถานะ 207 แล้วรายงานความสำเร็จและความล้มเหลวในเนื้อหาการตอบสนอง หากคุณใช้การดำเนินการ POST วิธี 207 ก็ใช้ได้ คุณจะต้องออกแบบเนื้อหาตอบสนองของคุณเองเพื่อสื่อสารว่าการดำเนินการใดประสบความสำเร็จและล้มเหลว ฉันไม่ทราบถึงมาตรฐาน


ขอบคุณมาก. โดยThis can be done in a RESTful wayที่คุณหมายถึงการปรับปรุงและสร้างจะต้องทำแยกต่างหาก?
Sam R.

1
@norbertpy การดำเนินการเขียนบางประเภทบนทรัพยากรอาจทำให้ทรัพยากรอื่น ๆ ได้รับการอัปเดตและสร้างขึ้นจากคำขอเดียว REST ไม่มีปัญหากับสิ่งนั้น ตัวเลือกวลีของฉันเป็นเพราะเฟรมเวิร์กบางส่วนใช้การดำเนินการจำนวนมากโดยการทำให้คำขอ HTTP เป็นอนุกรมลงในเอกสารหลายส่วนจากนั้นจึงส่งคำขอ HTTP แบบอนุกรมเป็นชุด ฉันคิดว่าแนวทางนั้นละเมิดข้อ จำกัด REST ในการระบุทรัพยากร
Darrel Miller

19

PUTไอเอ็นจี

PUT /binders/{id}/docs สร้างหรืออัปเดตและเชื่อมโยงเอกสารเดียวกับเครื่องผูก

เช่น:

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

PATCH ing

PATCH /docs สร้างเอกสารหากไม่มีอยู่และเชื่อมโยงกับตัวประสาน

เช่น:

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 } }
] 

ฉันจะรวมข้อมูลเชิงลึกเพิ่มเติมในภายหลัง แต่ในระหว่างนี้หากคุณต้องการโปรดดูRFC 5789 , RFC 6902และ William Durand's Please อย่าปะเหมือนรายการบล็อกIdiot


2
บางครั้งลูกค้าต้องการการดำเนินการจำนวนมากและไม่ต้องการดูแลว่าทรัพยากรจะอยู่ที่นั่นหรือไม่ ที่ผมกล่าวว่าในคำถามที่ลูกค้าต้องการที่จะส่งพวงของและเชื่อมโยงพวกเขาด้วยdocs bindersลูกค้าต้องการสร้างสารยึดเกาะหากไม่มีอยู่และทำการเชื่อมโยงหากมี ในหนึ่งคำขอเป็นกลุ่มเดียว
Sam R.

12

ในโครงการที่ฉันทำงานเราได้แก้ไขปัญหานี้โดยใช้สิ่งที่เราเรียกว่าคำขอ 'แบทช์' เรากำหนดเส้นทาง/batchที่เรายอมรับ json ในรูปแบบต่อไปนี้:

[  
   {
      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
      }
   },
]

การตอบกลับมีรหัสสถานะ 207 (หลายสถานะ) และมีลักษณะดังนี้:

[  
   {
      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
   },
]

คุณยังสามารถเพิ่มการสนับสนุนสำหรับส่วนหัวในโครงสร้างนี้ เรานำสิ่งที่พิสูจน์แล้วว่ามีประโยชน์ซึ่งเป็นตัวแปรที่จะใช้ระหว่างคำขอในชุดงานซึ่งหมายความว่าเราสามารถใช้การตอบสนองจากคำขอหนึ่งเป็นข้อมูลเข้าสู่อีกคำขอหนึ่งได้

Facebook และ Google มีการใช้งานที่คล้ายกัน:
https://developers.google.com/gmail/api/guides/batch
https://developers.facebook.com/docs/graph-api/making-multiple-requests

เมื่อคุณต้องการสร้างหรืออัปเดตทรัพยากรด้วยการเรียกเดียวกันฉันจะใช้ POST หรือ PUT ขึ้นอยู่กับกรณี หากมีเอกสารอยู่แล้วคุณต้องการให้เอกสารทั้งหมดเป็น:

  1. ถูกแทนที่ด้วยเอกสารที่คุณส่ง (เช่นคุณสมบัติที่ขาดหายไปในคำขอจะถูกลบออกและถูกเขียนทับที่มีอยู่แล้ว)?
  2. รวมกับเอกสารที่คุณส่งเข้ามา (เช่นคุณสมบัติที่ขาดหายไปในคำขอจะไม่ถูกลบออกและคุณสมบัติที่มีอยู่แล้วจะถูกเขียนทับ)?

ในกรณีที่คุณต้องการพฤติกรรมจากทางเลือกที่ 1 คุณควรใช้ POST และในกรณีที่คุณต้องการพฤติกรรมจากทางเลือก 2 คุณควรใช้ PUT

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

ตามที่มีคนแนะนำไปแล้วว่าคุณสามารถใช้ PATCH ได้ แต่ฉันชอบที่จะทำให้ API เรียบง่ายและไม่ใช้คำกริยาเพิ่มเติมหากไม่จำเป็น


5
ชอบคำตอบนี้สำหรับ Proof-of-Concept รวมถึงลิงก์ Google และ Facebook แต่ไม่เห็นด้วยกับตอนจบเกี่ยวกับ POST หรือ PUT ใน 2 กรณีนี้คำตอบที่กล่าวถึงคำตอบแรกควรเป็น PUT และที่สองควรเป็น PATCH
RayLuo

@RayLuo คุณช่วยอธิบายได้ไหมว่าทำไมเราถึงต้องการ PATCH นอกเหนือจาก POST และ PUT
David Berg

2
เพราะนั่นคือสิ่งที่ PATCH ถูกคิดค้นขึ้นมาเพื่อ คุณสามารถอ่านคำจำกัดความนี้และดูว่า PUT และ PATCH ตรงกับสัญลักษณ์แสดงหัวข้อย่อย 2 รายการของคุณอย่างไร
RayLuo

@DavidBerg, ดูเหมือนว่า Google --batch_xxxxได้แนะนำวิธีการอื่นที่จะร้องขอชุดกระบวนการคือแยกส่วนหัวและร่างกายของแต่ละคำขอย่อยไปยังส่วนที่เกี่ยวข้องของคำขอหลักที่มีขอบเขตเช่น มีความแตกต่างที่สำคัญระหว่างโซลูชันของ Google และ Facebook หรือไม่? เพิ่มเติมเกี่ยวกับ "ใช้การตอบกลับจากคำขอหนึ่งเป็นข้อมูลป้อนเข้าสู่อีกคำขอหนึ่ง" ฟังดูน่าสนใจมากคุณช่วยแบ่งปันรายละเอียดเพิ่มเติมได้ไหม หรือควรใช้สถานการณ์แบบไหน
ยาง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.