สรุปโดยย่อ
URL ของ REST API ควรมีคำนาม (ทรัพยากร) ไม่ใช่คำกริยา (การกระทำ) เมธอด HTTP (GET, POST, PUT, DELETE) คือคำกริยา การใช้คำกริยาที่บ่งบอกการกระทำ เช่น /getUser หรือ /createOrder เป็นการละเมิดหลักการของ REST, ทำให้เกิดความไม่สอดคล้องกัน และทำให้ API บำรุงรักษายากขึ้น Modern PetstoreAPI ใช้ URL ที่เน้นทรัพยากรตลอดทั้งระบบ
บทนำ
คุณกำลังออกแบบ API endpoint เพื่อค้นหาสัตว์เลี้ยงตามสถานะ สัญชาตญาณแรกของคุณอาจเป็น: GET /findPetsByStatus?status=available ซึ่งเป็นคำอธิบายที่ชัดเจนและบอกได้ว่าทำอะไรได้บ้าง แต่มันผิด
REST API ควรใช้คำนามใน URL ไม่ใช่คำกริยา เมธอด HTTP คือคำกริยา GET /pets?status=available คือการออกแบบที่ถูกต้อง URL แสดงถึงทรัพยากร (สัตว์เลี้ยง) และเมธอดแสดงถึงการกระทำ (รับข้อมูล)
Swagger Petstore รุ่นเก่าทำผิดพลาดตรงนี้ด้วย endpoint เช่น /pet/findByStatus และ /pet/findByTags การใช้คำกริยาที่บ่งบอกการกระทำใน URL เหล่านี้ละเมิดหลักการของ REST และสร้างปัญหาในการบำรุงรักษา Modern PetstoreAPI แก้ไขปัญหานี้โดยใช้ URL ที่เน้นทรัพยากรอย่างสม่ำเสมอ
ในคู่มือนี้ คุณจะได้เรียนรู้ว่าทำไมคำกริยาจึงไม่ควรอยู่ใน URL ของ REST, วิธีการออกแบบ endpoint ที่เน้นทรัพยากร, และวิธีที่ Modern PetstoreAPI นำสิ่งนี้ไปใช้อย่างถูกต้อง
ปัญหาคำกริยาใน REST API
คำกริยาที่บ่งบอกการกระทำใน URL แสดงว่าคุณกำลังคิดในแง่ของ RPC (Remote Procedure Call) ไม่ใช่ในแง่ของ REST
URL สไตล์ RPC (ผิด)
POST /createUser
GET /getUser?id=123
PUT /updateUser
DELETE /deleteUser?id=123
GET /findUsersByRole?role=admin
POST /sendEmail
GET /calculateTotal
URL เหล่านี้อธิบายถึงการกระทำ พวกมันอ่านเหมือนการเรียกฟังก์ชัน: createUser(), getUser(), sendEmail()
URL สไตล์ REST (ถูกต้อง)
POST /users
GET /users/123
PUT /users/123
DELETE /users/123
GET /users?role=admin
POST /emails
GET /orders/123/total
URL เหล่านี้อธิบายถึงทรัพยากร เมธอด HTTP ให้การกระทำ
ทำไมเรื่องนี้ถึงสำคัญ
ความสอดคล้องกัน: URL ของ REST มีรูปแบบที่คาดเดาได้ เมื่อคุณรู้ชื่อทรัพยากร คุณก็จะรู้ endpoint ทั้งหมด:
GET /pets- แสดงสัตว์เลี้ยงPOST /pets- สร้างสัตว์เลี้ยงGET /pets/{id}- รับข้อมูลสัตว์เลี้ยงPUT /pets/{id}- อัปเดตสัตว์เลี้ยงDELETE /pets/{id}- ลบสัตว์เลี้ยง
ด้วย URL ที่อิงคำกริยา ทุก endpoint จะไม่ซ้ำกัน ไม่มีรูปแบบให้ปฏิบัติตาม
ความสามารถในการขยายขนาด: เมื่อ API ของคุณเติบโตขึ้น URL ที่อิงคำกริยาก็จะเพิ่มจำนวนขึ้น:
/findPetsByStatus/findPetsByTags/findPetsByOwner/findPetsByBreed/searchPets/queryPets
URL ที่เน้นทรัพยากรใช้พารามิเตอร์การค้นหา:
GET /pets?status=availableGET /pets?tags=friendlyGET /pets?owner=johnGET /pets?breed=labrador
มี endpoint เดียวที่จัดการการกรองทั้งหมด
ทำไมเมธอด HTTP ถึงเป็นคำกริยา
REST ใช้ประโยชน์จากคำกริยาในตัวของ HTTP คุณไม่จำเป็นต้องสร้างขึ้นมาเอง
เมธอด HTTP สอดคล้องกับการดำเนินการ CRUD
POST → สร้าง
GET → อ่าน
PUT → อัปเดต (แทนที่)
PATCH → อัปเดต (บางส่วน)
DELETE → ลบ
เมธอดเหล่านี้มีความหมายที่กำหนดไว้ GET ปลอดภัยและ idempotent POST สร้างทรัพยากร DELETE ลบทรัพยากร
ตัวอย่าง: การจัดการผู้ใช้
ผิด (คำกริยาใน URL):
POST /createUser
GET /getUser?id=123
POST /updateUser
POST /deleteUser
ทุกการดำเนินการใช้ URL ที่แตกต่างกัน เมธอด HTTP ไม่สื่อความหมาย
ถูกต้อง (เมธอด HTTP เป็นคำกริยา):
POST /users ← สร้างผู้ใช้
GET /users/123 ← รับข้อมูลผู้ใช้
PUT /users/123 ← อัปเดตผู้ใช้
DELETE /users/123 ← ลบผู้ใช้
URL ยังคงเดิม เมธอดเปลี่ยนแปลง
ประโยชน์ของการใช้เมธอด HTTP
1. การแคช: คำขอ GET สามารถถูกแคชได้ เบราว์เซอร์และพร็อกซีรู้เรื่องนี้ หากคุณใช้ POST /getUser การแคชจะทำงานผิดพลาด
2. Idempotency: PUT และ DELETE เป็นแบบ idempotent การเรียกใช้หลายครั้งมีผลเช่นเดียวกับการเรียกใช้ครั้งเดียว สิ่งนี้มีความสำคัญสำหรับตรรกะการลองใหม่
3. ความปลอดภัย: GET ปลอดภัย—ไม่เปลี่ยนแปลงสถานะ เครื่องมือและเว็บครอว์เลอร์สามารถเรียกใช้ endpoint แบบ GET ได้อย่างปลอดภัย
4. การปฏิบัติตามมาตรฐาน: ไคลเอนต์ HTTP, พร็อกซี, และแคชเข้าใจเมธอด HTTP พวกเขาไม่เข้าใจคำกริยาที่คุณกำหนดเอง
ตัวอย่างจริงจาก Swagger Petstore
Swagger Petstore รุ่นเก่ามี endpoint ที่อิงคำกริยาหลายรายการ
ตัวอย่างที่ 1: การค้นหาสัตว์เลี้ยงตามสถานะ
Swagger Petstore (ผิด):
GET /pet/findByStatus?status=available
ปัญหา:
findByStatusเป็นวลีคำกริยา- ไม่สอดคล้องกับ endpoint
/pet/{id} - ไม่สามารถขยายได้ง่าย (แล้วการค้นหาตามเกณฑ์อื่นล่ะ?)
Modern PetstoreAPI (ถูกต้อง):
GET /pets?status=AVAILABLE
ประโยชน์:
- เน้นทรัพยากร (
/pets) - ใช้พารามิเตอร์การค้นหาสำหรับการกรอง
- สอดคล้องกับ endpoint อื่นๆ
- ขยายได้ง่าย:
GET /pets?status=AVAILABLE&species=dog
ดู เอกสาร REST ของ Modern PetstoreAPI สำหรับการใช้งานที่สมบูรณ์
ตัวอย่างที่ 2: การค้นหาสัตว์เลี้ยงตามแท็ก
Swagger Petstore (ผิด):
GET /pet/findByTags?tags=tag1,tag2
Modern PetstoreAPI (ถูกต้อง):
GET /pets?tags=friendly,trained
ตัวอย่างที่ 3: การเข้าสู่ระบบผู้ใช้
Swagger Petstore (ผิด):
GET /user/login?username=john&password=secret
ปัญหาหลายประการ:
loginเป็นคำกริยา- ใช้
GETสำหรับการยืนยันตัวตน (หายนะด้านความปลอดภัย) - รหัสผ่านอยู่ในพารามิเตอร์การค้นหาของ URL
Modern PetstoreAPI (ถูกต้อง):
POST /auth/login
Content-Type: application/json
{
"username": "john",
"password": "secret123"
}
ประโยชน์:
- เน้นทรัพยากร (
/auth) - เมธอด HTTP ที่ถูกต้อง (
POST) - ข้อมูลรับรองอยู่ในเนื้อหาคำขอ ไม่ใช่ URL
- ส่งคืนโทเค็น JWT สำหรับคำขอในภายหลัง
Modern PetstoreAPI แก้ไขปัญหานี้อย่างไร
Modern PetstoreAPI ใช้ URL ที่เน้นทรัพยากรตลอดทั้งระบบ
การจัดการสัตว์เลี้ยง
GET /pets ← แสดงสัตว์เลี้ยงทั้งหมด
GET /pets?status=AVAILABLE ← กรองตามสถานะ
GET /pets?species=dog ← กรองตามชนิด
GET /pets/{id} ← รับข้อมูลสัตว์เลี้ยงเฉพาะ
POST /pets ← สร้างสัตว์เลี้ยงใหม่
PUT /pets/{id} ← อัปเดตสัตว์เลี้ยง
PATCH /pets/{id} ← อัปเดตบางส่วน
DELETE /pets/{id} ← ลบสัตว์เลี้ยง
ไม่มีคำกริยา มีเพียงทรัพยากรและเมธอด HTTP
การจัดการคำสั่งซื้อ
GET /orders ← แสดงรายการคำสั่งซื้อ
GET /orders/{id} ← รับคำสั่งซื้อ
POST /orders ← สร้างคำสั่งซื้อ
PUT /orders/{id} ← อัปเดตคำสั่งซื้อ
DELETE /orders/{id} ← ยกเลิกคำสั่งซื้อ
GET /orders/{id}/items ← รับรายการสินค้าในคำสั่งซื้อ
การดำเนินการที่ซับซ้อน
สำหรับการดำเนินการที่ไม่สอดคล้องกับ CRUD อย่างชัดเจน Modern PetstoreAPI ใช้ทรัพยากรย่อย:
POST /orders/{id}/payment ← ประมวลผลการชำระเงินสำหรับคำสั่งซื้อ
POST /orders/{id}/shipment ← สร้างการจัดส่งสำหรับคำสั่งซื้อ
POST /pets/{id}/adoption ← เริ่มกระบวนการรับเลี้ยงบุตรบุญธรรม
สิ่งเหล่านี้ยังคงเน้นทรัพยากร /orders/{id}/payment แสดงถึงทรัพยากรการชำระเงินสำหรับคำสั่งซื้อ
เมื่อคำกริยาดูเหมือนจำเป็น
การดำเนินการบางอย่างไม่เหมาะกับโมเดล CRUD นี่คือวิธีการจัดการโดยไม่มีคำกริยาใน URL
การดำเนินการค้นหา
ผิด:
GET /searchPets?query=labrador
ตัวเลือกที่ถูกต้อง 1 (พารามิเตอร์การค้นหา):
GET /pets?search=labrador
ตัวเลือกที่ถูกต้อง 2 (ทรัพยากรการค้นหา):
GET /pets/search?q=labrador
ตัวเลือกที่ถูกต้อง 3 (เมธอด QUERY):
QUERY /pets
Content-Type: application/json
{
"query": "labrador",
"filters": {
"status": "AVAILABLE"
}
}
Modern PetstoreAPI รองรับทั้งสามรูปแบบขึ้นอยู่กับความซับซ้อน
การคำนวณ
ผิด:
GET /calculateShipping?weight=10&destination=NY
ถูกต้อง:
GET /shipping-estimates?weight=10&destination=NY
ทรัพยากรคือ "ประมาณการค่าจัดส่ง" ไม่ใช่การกระทำในการคำนวณ
การดำเนินการแบบกลุ่ม
ผิด:
POST /batchDeletePets
ถูกต้อง:
DELETE /pets?ids=1,2,3
หรือใช้ทรัพยากรแบบกลุ่ม:
POST /pets/batch-operations
Content-Type: application/json
{
"operation": "delete",
"ids": [1, 2, 3]
}
การกระทำที่เปลี่ยนแปลงสถานะ
ผิด:
POST /activateUser
POST /deactivateUser
ถูกต้อง:
PATCH /users/{id}
Content-Type: application/json
{
"status": "ACTIVE"
}
การเปลี่ยนแปลงสถานะคือการอัปเดตทรัพยากร
การทดสอบการออกแบบ URL ด้วย Apidog
Apidog ช่วยให้คุณตรวจสอบความถูกต้องของการออกแบบ REST API และตรวจจับการใช้คำกริยาใน URL
นำเข้า Modern PetstoreAPI
- นำเข้า OpenAPI spec ของ Modern PetstoreAPI
- Apidog สร้างกรณีทดสอบโดยอัตโนมัติ
- ตรวจสอบโครงสร้างและการตั้งชื่อ endpoint
ตรวจสอบคำกริยาใน URL
สร้างกฎการตรวจสอบแบบกำหนดเองใน Apidog:
// ตรวจสอบว่า URL มีคำกริยาที่บ่งบอกการกระทำทั่วไปหรือไม่
const verbs = ['get', 'create', 'update', 'delete', 'find', 'search',
'calculate', 'process', 'send', 'fetch'];
const url = request.url.toLowerCase();
for (const verb of verbs) {
if (url.includes(`/${verb}`)) {
throw new Error(`URL มีคำกริยา: ${verb} ให้ใช้ URL ที่เน้นทรัพยากรแทน`);
}
}
ทดสอบความสอดคล้องของ Endpoint
Apidog สามารถตรวจสอบได้ว่า endpoint ที่เกี่ยวข้องปฏิบัติตามรูปแบบที่สอดคล้องกัน:
✓ GET /pets
✓ POST /pets
✓ GET /pets/{id}
✓ PUT /pets/{id}
✓ DELETE /pets/{id}
ทั้งหมดใช้ทรัพยากรฐานเดียวกัน (/pets)
เปรียบเทียบกับ Modern PetstoreAPI
- นำเข้า API ของคุณและ Modern PetstoreAPI เข้าสู่ Apidog
- เปรียบเทียบโครงสร้าง endpoint เคียงข้างกัน
- ระบุความไม่สอดคล้องกันและการใช้คำกริยา
- ปรับปรุง API ของคุณให้ตรงกับหลักการของ REST
กลยุทธ์การย้ายระบบ
หากคุณมี API ที่มีอยู่ซึ่งใช้คำกริยาใน URL นี่คือวิธีที่จะย้ายระบบ
กลยุทธ์ที่ 1: การกำหนดเวอร์ชัน
สร้าง API เวอร์ชันใหม่ด้วย URL ที่ถูกต้อง:
# API เก่า (v1)
GET /api/v1/findPetsByStatus?status=available
# API ใหม่ (v2)
GET /api/v2/pets?status=available
บำรุงรักษา v1 เพื่อความเข้ากันได้แบบย้อนหลัง และส่งเสริมให้ย้ายไปใช้ v2
กลยุทธ์ที่ 2: การตั้งชื่อแทน (Aliasing)
รองรับทั้ง URL เก่าและใหม่ชั่วคราว:
# URL เก่า (เลิกใช้แล้ว)
GET /pet/findByStatus?status=available
# URL ใหม่ (ที่แนะนำ)
GET /pets?status=available
ส่งคืนคำเตือนการเลิกใช้งานในการตอบกลับ:
{
"data": [...],
"warnings": [
{
"code": "DEPRECATED_ENDPOINT",
"message": "endpoint นี้เลิกใช้งานแล้ว โปรดใช้ GET /pets?status=available แทน",
"sunset": "2027-01-01"
}
]
}
กลยุทธ์ที่ 3: การเปลี่ยนเส้นทาง (Redirects)
ใช้ HTTP 301 redirects สำหรับการย้ายระบบที่ไม่ซับซ้อน:
GET /pet/findByStatus?status=available
→ 301 ถูกย้ายถาวร
Location: /pets?status=available
วิธีนี้ใช้ได้กับคำขอ GET แต่ใช้ไม่ได้กับ POST, PUT, หรือ DELETE
บทสรุป
URL ของ REST API ควรมีคำนาม (ทรัพยากร) ไม่ใช่คำกริยา (การกระทำ) เมธอด HTTP ให้คำกริยา หลักการนี้สร้าง API ที่สอดคล้องกัน ขยายขนาดได้ และบำรุงรักษาง่าย
Swagger Petstore รุ่นเก่าละเมิดหลักการนี้ด้วย endpoint เช่น /pet/findByStatus Modern PetstoreAPI แก้ไขปัญหานี้โดยใช้ URL ที่เน้นทรัพยากรตลอดทั้งระบบ: /pets?status=AVAILABLE
ประเด็นสำคัญ:
- ใช้คำนามใน URL:
/pets,/orders,/users - ให้เมธอด HTTP เป็นคำกริยา:
GET,POST,PUT,DELETE - ใช้พารามิเตอร์การค้นหาสำหรับการกรอง:
/pets?status=available - สำหรับการดำเนินการที่ซับซ้อน ให้ใช้ทรัพยากรย่อย:
/orders/{id}/payment - ทดสอบการออกแบบ API ของคุณด้วย Apidog เพื่อตรวจจับการใช้คำกริยาตั้งแต่เนิ่นๆ
ดู เอกสารประกอบ Modern PetstoreAPI สำหรับตัวอย่างที่สมบูรณ์ของการออกแบบ URL ที่เน้นทรัพยากร
คำถามที่พบบ่อย
ฉันสามารถใช้คำกริยาใน URL ของ REST ได้เลยหรือไม่?
ไม่ค่อยบ่อยนัก หากการดำเนินการนั้นไม่เข้ากับโมเดลทรัพยากรอย่างแท้จริง (เช่น /search หรือ /login) การใช้คำกริยาอาจเป็นที่ยอมรับได้ แต่ 95% ของเวลา คุณสามารถสร้างมันเป็นทรัพยากรได้
แล้ว /login และ /logout ล่ะ?
สิ่งเหล่านี้เป็นข้อยกเว้นทั่วไป API จำนวนมากใช้ /auth/login และ /auth/logout อีกทางเลือกหนึ่งคือ สร้างมันเป็นทรัพยากร: POST /sessions (เข้าสู่ระบบ) และ DELETE /sessions/{id} (ออกจากระบบ)
ฉันจะจัดการกับคำค้นที่ซับซ้อนได้อย่างไร?
ใช้พารามิเตอร์การค้นหาสำหรับการกรองแบบง่าย: /pets?status=available&species=dog สำหรับคำค้นที่ซับซ้อน ให้ใช้เมธอด HTTP QUERY หรือทรัพยากรการค้นหา: POST /pets/search
จะทำอย่างไรหากการดำเนินการของฉันไม่สอดคล้องกับ CRUD?
สร้างมันเป็นทรัพยากรย่อย แทนที่จะเป็น POST /processPayment ให้ใช้ POST /orders/{id}/payment การชำระเงินเป็นทรัพยากรที่เกี่ยวข้องกับคำสั่งซื้อ
ฉันจะทดสอบได้อย่างไรว่า URL ของฉันเน้นทรัพยากร?
ใช้ Apidog เพื่อนำเข้า OpenAPI spec ของคุณและตรวจสอบคำกริยาใน URL เปรียบเทียบโครงสร้าง API ของคุณกับ Modern PetstoreAPI เพื่อใช้อ้างอิง
ฉันควรใช้ /pets/search หรือ /pets?search=query?
ทั้งสองแบบเป็นที่ยอมรับได้ /pets?search=query นั้นง่ายกว่าสำหรับการค้นหาพื้นฐาน /pets/search หรือ QUERY /pets ทำงานได้ดีกว่าสำหรับการค้นหาที่ซับซ้อนซึ่งมีพารามิเตอร์หลายตัว
ฉันจะย้ายจาก URL ที่อิงคำกริยาได้อย่างไร?
ใช้การกำหนดเวอร์ชัน API (/v2/pets แทน /v1/findPets), เพิ่มคำเตือนการเลิกใช้งาน, และให้เวลาไคลเอนต์ในการย้ายระบบ ดูรายละเอียดในส่วนกลยุทธ์การย้ายระบบ
Modern PetstoreAPI ใช้คำกริยาใดๆ ใน URL หรือไม่?
Modern PetstoreAPI หลีกเลี่ยงการใช้คำกริยาใน URL การดำเนินการต่างๆ เช่น การค้นหา, การกรอง, และการยืนยันตัวตน ถูกจำลองเป็นทรัพยากรหรือใช้พารามิเตอร์การค้นหา ตรวจสอบ เอกสาร REST API สำหรับตัวอย่าง
