RESTful API ฉันควรส่งคืนวัตถุที่สร้าง / อัพเดทหรือไม่


35

ฉันกำลังออกแบบเว็บเซอร์วิส RESTful โดยใช้ WebApi และสงสัยว่าการตอบสนอง HTTP และร่างกายการตอบสนองที่จะกลับมาเมื่อมีการปรับปรุง / สร้างวัตถุ

ตัวอย่างเช่นฉันสามารถใช้วิธีการโพสต์เพื่อส่ง JSON บางส่วนไปยังบริการเว็บแล้วสร้างวัตถุ เป็นวิธีที่ดีที่สุดหรือไม่ที่จะตั้งค่าสถานะ HTTP เป็นสร้าง (201) หรือตกลง (200) และเพียงแค่ส่งคืนข้อความเช่น "เพิ่มพนักงานใหม่" หรือส่งคืนวัตถุที่ถูกส่งมาตั้งแต่แรก?

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

ความคิดใด ๆ

ตัวอย่าง:

เพิ่มพนักงานใหม่:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

อัพเดทพนักงานที่มีอยู่:

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

คำตอบ:

การตอบสนองกับวัตถุที่สร้าง / อัพเดท

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

การตอบสนองด้วยข้อความเพียง:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

การตอบสนองด้วยรหัสสถานะเพียง:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT

2
เป็นคำถามที่ดี แต่การใช้คำว่า "แนวปฏิบัติที่ดีที่สุด" นั้นเป็นข้อห้ามในเว็บไซต์นี้meta.programmers.stackexchange.com/questions/2442/คุณอาจต้องการคำถามใหม่ meta.programmers.stackexchange.com/questions/6967/…
Snoop

3
ควรติดตามการตั้งค่าสถานะเพื่อขอ (เช่น) แอปพลิเคชันมือถือสามารถรับวัตถุทั้งหมดที่ส่งคืนเมื่อใช้ WiFi แต่ใช้ ID เมื่อใช้ข้อมูลเซลลูลาร์เท่านั้น มีส่วนหัวที่ควรใช้เพื่อหลีกเลี่ยงการทำให้สกปรก JSON หรือไม่?
แอนดรูพูดว่า Reinstate Monica

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

@AndrewPiliser ความคิดของคุณคล้ายกับUPDATE/INSERT ... RETURNINGตัวแปรPostgresql สำหรับ SQL มันมีประโยชน์อย่างมากโดยเฉพาะอย่างยิ่งมันช่วยให้การส่งข้อมูลใหม่และขออะตอมรุ่นปรับปรุง
beldaz

คำตอบ:


31

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

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


+1 วัตถุประสงค์ของการเกลียดชังในการไม่ให้ลูกค้าเขียน uri นั้นสามารถทำได้โดยการอนุญาตให้ลูกค้ากรอกข้อมูลลงในเซิร์ฟเวอร์ที่ให้แม่แบบ URL ด้วยรหัสเฉพาะ ใช่ลูกค้า "เรียบเรียง" แต่ใช้วิธี "เติมในช่องว่าง" เท่านั้น ในขณะที่ HATEOAS ไม่บริสุทธิ์มันบรรลุวัตถุประสงค์และทำให้การทำงานกับวัตถุที่มีจำนวนมากของ "การกระทำ" uri เป็นแบนด์วิดธ์ที่มีความสำคัญน้อยกว่าเล็กน้อยไม่พูดถึงเมื่อคุณใส่วัตถุเหล่านั้นในรายการ (ขนาดใหญ่)
Marjan Venema

3
+1 ในคำแนะนำ "โปรดอย่าสร้างข้อความโดยพลการ"
HairOfTheDog

"ไม่มีข้อความที่กำหนดเอง" เน้นข้อความสตริงหรือค่าส่งคืนที่ไม่ใช่ทรัพยากรที่สร้างขึ้นหรือไม่ ฉันมุ่งเน้นไปที่กรณีที่เราส่งคืนรหัสของทรัพยากรที่สร้างขึ้น (แต่ไม่ใช่ตัวทรัพยากร) และสงสัยว่าสิ่งนี้เหมาะกับอะไร
Flater

12

ส่วนตัวผมมักจะ200 OKกลับมาเท่านั้น

เพื่ออ้างคำถามของคุณ

พิจารณาข้อเท็จจริงที่ว่าผู้ใช้รู้ว่าวัตถุใดที่พวกเขากำลังพยายามสร้าง / อัปเดตอยู่ดี

ทำไมต้องเพิ่มแบนด์วิดท์เพิ่มเติม (ซึ่งอาจต้องจ่าย) เพื่อบอกลูกค้าว่ามันรู้อะไรไปแล้ว?


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

3
ดูstackoverflow.com/a/827045/290182เกี่ยวกับ200/ 204 No Contentเพื่อหลีกเลี่ยงความสับสน jQuery และสิ่งที่คล้ายกัน
beldaz

10

@ iswinky ฉันจะส่ง payload กลับมาเสมอในกรณีของ POST และ PUT

ในกรณีของ POST คุณอาจสร้างเอนทิตีที่มี ID ภายในหรือ UUID ดังนั้นจึงเหมาะสมที่จะส่ง payload กลับ

ในทำนองเดียวกันในกรณีของ PUT คุณอาจละเว้นบางฟิลด์ของผู้ใช้ (ค่าไม่เปลี่ยนรูปพูด) หรือในกรณีของ PATCH ข้อมูลอาจถูกเปลี่ยนแปลงโดยผู้ใช้รายอื่นเช่นกัน

การส่งคืนข้อมูลตามที่ได้รับการยืนยันเป็นความคิดที่ดีเสมอและไม่เป็นอันตรายอย่างแน่นอน หากผู้เรียกไม่ต้องการข้อมูลที่ส่งคืนนี้เขา / เธอจะไม่ประมวลผล แต่จะใช้ statusCode มิฉะนั้นพวกเขาสามารถใช้ข้อมูลนี้เป็นสิ่งที่จะอัปเดต UI ด้วย

เฉพาะในกรณีของการลบที่ฉันจะไม่ส่งคืนน้ำหนักบรรทุกและจะทำอย่างใดอย่างหนึ่ง 200 กับเนื้อหาการตอบสนองหรือ 204 ที่ไม่มีเนื้อหาการตอบสนอง

แก้ไข: ขอบคุณความคิดเห็นบางส่วนจากด้านล่างฉัน rewording คำตอบของฉัน ฉันยังคงยืนตามวิธีที่ฉันออกแบบ API ของฉันและส่งคำตอบกลับ แต่ฉันคิดว่ามันสมเหตุสมผลที่จะทำให้ความคิดการออกแบบของฉันมีคุณสมบัติ

ก) เมื่อฉันบอกว่าส่งคืนน้ำหนักบรรทุกฉันหมายถึงส่งข้อมูลของทรัพยากรกลับไม่ใช่น้ำหนักบรรทุกเดียวกันที่มาในคำขอ ตัวอย่าง: ถ้าคุณส่ง payload ในการสร้างฉันอาจจะสร้างเอนทิตี้อื่น ๆ เช่นแบ็กเอนด์ UUID และ (อาจ) timestamps และการเชื่อมต่อบางอย่าง (กราฟ) ฉันจะส่งกลับทั้งหมดนี้ในการตอบสนอง (ไม่เพียง แต่น้ำหนักบรรทุกตามคำขอ - ซึ่งไม่มีจุดหมาย)

b) ฉันจะไม่ส่งการตอบกลับในกรณีที่ส่วนของข้อมูลมีขนาดใหญ่มาก ฉันได้พูดถึงเรื่องนี้ในความคิดเห็น แต่สิ่งที่ฉันต้องการที่จะเตือนคือฉันจะพยายามอย่างดีที่สุดในการออกแบบ API หรือทรัพยากรของฉันเพื่อที่มันจะได้ไม่ต้องมีส่วนของข้อมูลขนาดใหญ่มาก ฉันจะพยายามแบ่งทรัพยากรออกเป็นเอนทิตีที่เล็กลงและสามารถจัดการได้ซึ่งทรัพยากรแต่ละอย่างจะถูกกำหนดโดยแอ็ตทริบิวต์ JSON 15-20 และไม่ใหญ่กว่า

ในกรณีที่วัตถุมีขนาดใหญ่มากหรือวัตถุแม่กำลังได้รับการปรับปรุงแล้วฉันจะส่งกลับโครงสร้างที่ซ้อนกันเป็น hrefs

บรรทัดล่างคือฉันจะพยายามส่งข้อมูลที่เหมาะสมสำหรับผู้บริโภค / UI ในการประมวลผลทันทีและดำเนินการกับแอ็คชั่นอะตอมมิก API แทนที่จะต้องไปแล้วดึง API เพิ่มเติมอีก 2-5 เพื่ออัปเดตโพสต์ UI การสร้าง / อัพเดทข้อมูล

API ของเซิร์ฟเวอร์ต่อเซิร์ฟเวอร์อาจคิดแตกต่างกันในเรื่องนี้ ฉันมุ่งเน้นไปที่ API ที่ผลักดันประสบการณ์ของผู้ใช้


ฉันสามารถเห็นหลาย ๆ สถานการณ์ที่การส่งคืนน้ำหนักบรรทุกทั้งหมดเป็นความคิดที่ไม่ดีเมื่อปริมาณบรรทุกมีขนาดใหญ่
beldaz

2
@beldaz เห็นด้วยอย่างสมบูรณ์ YMMV ขึ้นอยู่กับการออกแบบของ REST API ฉันมักจะหลีกเลี่ยงวัตถุที่มีขนาดใหญ่มากและแบ่งย่อยเป็นชุดของทรัพยากรย่อย / PUT หากส่วนของข้อมูลมีขนาดใหญ่มากมีวิธีที่ดีกว่าในการทำเช่นนี้และในนั้นคุณต้องการทำ HATEOAS (เช่น Marjan บอกไว้ด้านบน) ซึ่งคุณจะส่งคืนการอ้างอิงไปยังวัตถุแทนวัตถุนั้นเอง
ksprashu

@ksprashu: "ดังนั้นมันสมเหตุสมผลที่จะส่ง payload กลับ" - ฉันคิดว่านี่เป็นความคิดที่ไม่ดีเพราะทำให้ทรัพยากรสามารถรับได้หลายวิธี: ผ่าน GET ซึ่งเป็นการตอบสนองของ POST เป็นการตอบสนองของ PUT หมายความว่าลูกค้าได้รับ 3 ทรัพยากรที่มีโครงสร้างที่แตกต่างกัน โดยที่ถ้าคุณจะส่งคืน URI เท่านั้น (ที่ตั้ง) โดยไม่มีเนื้อหาดังนั้นวิธีเดียวในการรับทรัพยากรคือ GET สิ่งนี้จะทำให้แน่ใจว่าลูกค้าจะได้รับคำตอบที่สอดคล้องกันเสมอ
mentallurg

@mentallurg ฉันรู้ว่าฉันไม่ได้พูดถูก ขอบคุณที่ชี้นำ ฉันแก้ไขคำตอบของฉันแล้ว แจ้งให้เราทราบหากนี่เหมาะสมกว่า
ksprashu

ตราบใดที่คุณใช้บริการสำหรับงานที่บ้านของคุณก็ไม่สำคัญ ทำตามที่คุณต้องการ ประหยัดเวลาของคุณ
mentallurg

9

อ้างอิงถึงลิงก์RFC มาตรฐานคุณควรส่งคืนสถานะ 201 (สร้าง) เมื่อเก็บทรัพยากรคำขอเรียบร้อยแล้วโดยใช้โพสต์ ในแอปพลิเคชันส่วนใหญ่ id ของทรัพยากรถูกสร้างขึ้นโดยเซิร์ฟเวอร์เองดังนั้นจึงเป็นการดีที่จะส่งคืนรหัสของทรัพยากรที่สร้างขึ้น การส่งคืนวัตถุทั้งหมดเป็นค่าใช้จ่ายสำหรับคำขอโพสต์ แนวปฏิบัติที่เหมาะสมคือส่งคืนตำแหน่ง URLของทรัพยากรที่สร้างขึ้นใหม่

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

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

จุดสิ้นสุดส่วนที่เหลือนี้จะส่งคืนการตอบกลับเป็น:

สถานะ 201 - สร้างแล้ว

ตำแหน่งส่วนหัว→ http: // localhost: 8080 / พนักงาน / 1


ดีและสะอาด - จะติดตามสิ่งนี้ตั้งแต่นี้ไป
เรื่อย

0

ฉันจะทำให้น้ำหนักบรรทุกในร่างกายกลับมาตามเงื่อนไขกับพารามิเตอร์ HTTP

บ่อยครั้งที่ไม่ควรส่งคืนเนื้อหาบางประเภทกลับไปยังผู้บริโภค API เพื่อหลีกเลี่ยงการไปกลับโดยไม่จำเป็น (หนึ่งในเหตุผลที่ GraphQL มีอยู่)

ตามความเป็นจริงเมื่อแอปพลิเคชันของเรามีการใช้ข้อมูลมากขึ้นและกระจายมากขึ้นฉันลองปฏิบัติตามแนวทางนี้:

คำแนะนำของฉัน :

เมื่อใดก็ตามที่มีกรณีการใช้งานที่ต้องการ GET ทันทีหลังจาก POST หรือ PUT นั่นคือกรณีที่อาจเป็นการดีที่สุดที่จะส่งคืนบางสิ่งในการตอบสนอง POST / PUT

วิธีดำเนินการนี้และเนื้อหาประเภทใดที่จะส่งคืนกลับมาจาก PUT หรือ POST นั่นเป็นแอปพลิเคชันเฉพาะ ตอนนี้มันจะน่าสนใจถ้าแอปพลิเคชันสามารถกำหนดประเภทของ "เนื้อหา" ในเนื้อหาการตอบสนอง (เราต้องการเพียงตำแหน่งของวัตถุใหม่หรือบางส่วนของเขตข้อมูลหรือวัตถุทั้งหมด ฯลฯ )

แอปพลิเคชันสามารถกำหนดชุดของพารามิเตอร์ที่ POST / PUT สามารถรับเพื่อควบคุมประเภทของ "เนื้อหา" เพื่อส่งคืนในเนื้อหาการตอบสนอง หรืออาจเข้ารหัสเคียวรี GraphQL บางประเภทเป็นพารามิเตอร์ (ฉันเห็นว่านี่มีประโยชน์ แต่ก็กลายเป็นฝันร้ายการบำรุงรักษาด้วย)

ไม่ว่าจะด้วยวิธีใดดูเหมือนฉันว่า:

  1. มันก็โอเค (และเป็นที่ต้องการมากที่สุด) เพื่อส่งคืนบางสิ่งในเนื้อความการตอบสนอง POST / PUT
  2. วิธีการนี้ทำขึ้นเฉพาะแอปพลิเคชันและแทบจะเป็นไปไม่ได้ที่จะพูดคุยทั่วไป
  3. คุณไม่ต้องการส่งคืน "บริบท" ขนาดใหญ่เป็นค่าเริ่มต้น (เสียงรบกวนการจราจรที่เอาชนะเหตุผลทั้งหมดของการย้ายออกจาก POST-follow-by-GETs)

ดังนั้น 1) ทำ แต่ 2) ทำให้มันง่าย

ตัวเลือกอื่นที่ฉันได้เห็นคือคนที่สร้างจุดสิ้นสุดทางเลือก (เช่น / ลูกค้าสำหรับ POST / PUT ที่ไม่ส่งคืนอะไรในร่างกายและ / customer_with_details สำหรับ POST / PUT ถึง / ลูกค้า แต่กลับมาบางสิ่งในเนื้อความการตอบกลับ)

ฉันจะหลีกเลี่ยงวิธีการนี้ได้ จะเกิดอะไรขึ้นเมื่อคุณต้องกลับเนื้อหาประเภทอื่นอย่างถูกกฎหมาย? จุดปลายเดียวต่อประเภทเนื้อหาหรือไม่ ไม่สามารถปรับขนาดหรือบำรุงรักษาได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.