ออกแบบ RESTful query API พร้อมด้วยรายการพารามิเตอร์แบบยาว [ปิด]


153

ฉันต้องออกแบบ RESTful query API ซึ่งจะส่งคืนชุดของออบเจ็กต์ตามตัวกรองบางตัว วิธี HTTP ปกติสำหรับสิ่งนี้คือ GET ปัญหาเดียวก็คือมันสามารถมีตัวกรองอย่างน้อยหนึ่งโหลและถ้าเราผ่านมันทั้งหมดเป็นพารามิเตอร์การสืบค้น URL อาจยาวมาก (นานพอที่จะถูกบล็อกโดยไฟร์วอลล์บางส่วน)

การลดจำนวนพารามิเตอร์ไม่ใช่ตัวเลือก

อีกทางเลือกหนึ่งที่ฉันคิดว่าใช้ประโยชน์จากวิธีการโพสต์บน URI และส่งตัวกรองเป็นส่วนหนึ่งของเนื้อหาของโพสต์ นี่เป็นสิ่งที่ต่อต้านการ RESTfull (การเรียก POST เพื่อค้นหาข้อมูล)

ใครมีข้อเสนอแนะการออกแบบที่ดีกว่า?


2
ใช้ชื่อพารามิเตอร์แบบสั้น (1-char เป็นต้น)
Madbreaks

2
มันอาจจะไม่สงบจริง ๆ แต่ฉันคิดว่าคุณต้องมีประโยชน์เมื่อพูดถึง GET และ POST หากคุณมีตัวแปรมากมายที่จะส่งและคุณไม่สามารถลดได้ฉันจะโพสต์มัน ฉันไม่ชอบ overstuffing URL แต่นั่นเป็นเพียงฉัน
Doug Dawson

ขอบคุณ แม้ว่าคำถามนี้จะถูกปิด แต่เป็นคำถามที่ฉันต้องการคำตอบ ฉันดีใจที่คุณถาม
Casey Crookston

คำตอบ:


142

โปรดจำไว้ว่าด้วย REST API ทุกคำถามของมุมมองของคุณ

แนวคิดหลักสองข้อใน REST API คือจุดสิ้นสุดและทรัพยากร (เอนทิตี) จุดสิ้นสุดอย่างใดอย่างหนึ่งปลายทางส่งคืนทรัพยากรผ่าน GET หรือยอมรับทรัพยากรผ่าน POST และ PUT และอื่น ๆ (หรือการรวมกันของข้างต้น)

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

ข้อความอ้างอิงจากRFC 2616 (โดยไม่ใส่ส่วนที่ไม่เกี่ยวข้องออกและเน้นส่วนที่เกี่ยวข้อง):

9.5 โพสต์

เมธอด POST ใช้เพื่อร้องขอให้เซิร์ฟเวอร์ต้นทางยอมรับเอนทิตีที่อยู่ในคำขอเป็นผู้ใต้บังคับบัญชาใหม่ของทรัพยากรที่ระบุโดย Request-URI ใน Request-Line POST ได้รับการออกแบบมาเพื่อให้วิธีการแบบเดียวกันครอบคลุมฟังก์ชั่นต่อไปนี้:

  • ...
  • การจัดเตรียมบล็อกข้อมูลเช่นผลลัพธ์ของการส่งแบบฟอร์มไปยังกระบวนการจัดการข้อมูล
  • ...

...

การกระทำที่ดำเนินการโดยวิธีการโพสต์อาจจะไม่ส่งผลให้ทรัพยากรที่สามารถระบุด้วย URI ในกรณีนี้ 200 (OK) หรือ 204 (ไม่มีเนื้อหา) เป็นสถานะการตอบสนองที่เหมาะสมขึ้นอยู่กับว่าการตอบสนองนั้นมีเอนทิตีที่อธิบายผลลัพธ์หรือไม่

หากมีการสร้างทรัพยากรบนเซิร์ฟเวอร์ต้นทางการตอบสนองควรเป็น 201 (สร้างขึ้น) ...

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

ลองพิจารณาตัวอย่างต่อไปนี้:

GET    /books?author=AUTHOR
POST   /books
PUT    /books/ID
DELETE /books/ID

นี่คือ REST CRUD ทั่วไป อย่างไรก็ตามถ้าเราเพิ่ม:

POST /books/search

    {
        "keywords": "...",
        "yearRange": {"from": 1945, "to": 2003},
        "genre": "..."
    }

ไม่มีอะไรที่ไม่สงบสำหรับจุดสิ้นสุดนี้ ยอมรับข้อมูล (เอนทิตี) ในรูปแบบของเนื้อความที่ร้องขอ ข้อมูลนั้นเป็นเกณฑ์การค้นหา - DTO เหมือนกัน ปลายทางนี้ผลิตทรัพยากร (นิติบุคคล) ในการตอบสนองต่อการร้องขอ: ผลลัพธ์การค้นหา ทรัพยากรผลการค้นหาเป็นทรัพยากรชั่วคราวให้บริการแก่ลูกค้าทันทีโดยไม่มีการเปลี่ยนเส้นทางและไม่มีการเปิดเผยจาก url อื่นที่เป็นที่ยอมรับ

ยังคงเป็น REST ยกเว้นเอนทิตีไม่ใช่หนังสือ - เอนทิตีที่ร้องขอคือเกณฑ์การค้นหาหนังสือและเอนทิตีการตอบสนองคือผลลัพธ์การค้นหาหนังสือ


คุณช่วยแนะนำการตั้งชื่อคลาสสำหรับ DTO ได้ไหม?
Kwadz

ส่วนตัวผมจะไปกับและBooksSearchCriteriaDTO BooksSearchResultsDTO
อาเมียร์อาบีรี

รหัสตอบกลับ HTTP ที่ดีที่สุดสำหรับกรณีของ POST / books / search นี้คืออะไร ยังคงใช้ 201?
L. Holanda

9
201 เป็นสิ่งที่ตรงกันข้าม - มันบอกเป็นนัยว่ามีการสร้างทรัพยากร แหล่งข้อมูลที่คาดว่าจะมี URI ที่ไม่ซ้ำใคร 201 มีความเหมาะสมเมื่อPOSTใช้กับส่วน C ของ CRUD ฉันจะไปกับ 200 ธรรมดาธรรมดาอาจจะเป็น 204 สำหรับผลการค้นหาที่ว่างเปล่า
Amir Abiri

@AmirAbiri ขอบคุณมาก
mohamed-mhiri

84

ผู้คนจำนวนมากยอมรับการฝึกฝนที่ GET ที่มีสตริงข้อความค้นหายาวเกินไปหรือซับซ้อนเกินไป (เช่นสตริงข้อความค้นหาไม่จัดการข้อมูลซ้อนกันได้ง่าย) สามารถส่งเป็น POST แทนโดยมีข้อมูลซับซ้อน / ยาวแสดงอยู่ในเนื้อหา ของคำขอ

ค้นหาข้อมูลจำเพาะของ POST ใน HTTP spec มันกว้างอย่างไม่น่าเชื่อ (ถ้าคุณต้องการแล่นเรือประจัญบานผ่านช่องโหว่ใน REST ... ใช้ POST)

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

(ฮ่า ๆ การพูดนอกเรื่องยาว ... ฉันเพิ่งค้นพบว่าตามข้อกำหนด HTTP GET สามารถมีเนื้อหาเอกสารมีส่วนหนึ่งที่กล่าวว่าการถอดความ "คำขอใด ๆ สามารถมีเนื้อหาเอกสารยกเว้นรายการที่อยู่ในส่วนนี้" ... และส่วนที่อ้างถึงไม่มีรายการใด ๆ ฉันค้นหาและพบเธรดที่ผู้เขียน HTTP กำลังพูดถึงและเป็นเจตนาเพื่อให้เราเตอร์และผู้ใช้ดังกล่าวไม่ต้องแยกความแตกต่างระหว่างข้อความที่แตกต่างกันอย่างไรก็ตามใน ฝึกฝนชิ้นส่วนโครงสร้างพื้นฐานจำนวนมากจะดรอปเนื้อความของ GET ดังนั้นคุณสามารถใช้ GET พร้อมกับตัวกรองที่แสดงอยู่ในร่างกายเช่น POST แต่คุณจะกลิ้งลูกเต๋า)


11
ดูคำถามนี้สำหรับการสนทนาเพิ่มเติมเกี่ยวกับ HTTP GET พร้อมเนื้อหา
RickyA

6

โดยสรุป: สร้าง POST แต่แทนที่เมธอด HTTP โดยใช้ส่วนหัวX-HTTP-Method-Override

คำขอจริง

โพสต์ / หนังสือ

ร่างกายนิติบุคคล

{"title": "Ipsum", "year": 2017}

ส่วนหัว

X-HTTP-Method-Override: GET

บนฝั่งเซิร์ฟเวอร์ตรวจสอบว่าส่วนหัว X-HTTP-Method-Override มีอยู่แล้วใช้ค่าเป็นวิธีการสร้างเส้นทางไปยังจุดสิ้นสุดสุดท้ายในแบ็กเอนด์ นอกจากนี้ให้ใช้เอนทิตีเนื้อความเป็นสตริงแบบสอบถาม จากมุมมองแบ็กเอนด์คำขอกลายเป็นเพียงแค่รับ

วิธีนี้คุณออกแบบให้สอดคล้องกับหลักการ REST

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


2
IETF คัดค้านส่วนหัว HTTP HTTP ที่นำหน้าแล้ว: tools.ietf.org/html/rfc6648
jannis


@jannis RFC ที่คุณลิงก์อยู่ 1.4 มันไม่มีคำแนะนำเกี่ยวกับX-การลบที่มีอยู่ และ 1.5 มันไม่ได้แทนที่ข้อกำหนดที่มีอยู่ ... X-IMO จะยังคงอยู่ที่นี่
Jan Molnar

-3

หากคุณกำลังพัฒนาใน Java และ JAX-RS ฉันขอแนะนำให้คุณใช้ @QueryParam กับ @GET

ฉันมีคำถามเดียวกันเมื่อฉันต้องการผ่านรายการ

ดูตัวอย่าง:

import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/poc")
public class UserService {

    @GET
    @Path("/test/")
    @Produces(MediaType.APPLICATION_JSON)
    public Response test(@QueryParam("code") final List<Integer> code) {
                Integer int0 = codigo.get(0);
                Integer int1 = codigo.get(1);

        return Response.ok(new JSONObject().put("int01", int0)).build();
    }
}

รูปแบบ URI: “ poc / test? code = 1 & code = 2 & code = 3

@QueryParamจะแปลงพารามิเตอร์ข้อความค้นหา“ orderBy = age & orderBy = name” เป็น java.util.List โดยอัตโนมัติ


มันจะดีกว่าถ้าคุณอธิบายตัวอย่างของคุณ เขียนด้วยภาษาโปรแกรมอะไร?
Aleks Andreev

สวัสดี @AleksAndreev ขอบคุณสำหรับความคิดเห็นของคุณ ดีขึ้นไหม tks
acacio.martins

คำถามนี้เกี่ยวกับการออกแบบบริการ RESTful ไม่ใช่เกี่ยวกับการใช้งาน คำตอบนี้ไม่ตอบคำถาม
นอกรีตลิง

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