Đừng dùng POST cho mọi thứ! Giải ngố sự kết hợp của HTTP Methods và nghệ thuật RESTful Routing
Chào anh em Viblo, lại là mình đây!
Sau mấy bài ngụp lặn với các loại Query phức tạp trong Database, hôm nay chúng ta ngoi lên tầng trên một chút, nói về phần giao tiếp: API và Routing.
Nhớ lại hồi mới chập chững làm web bằng PHP "thuần", tư duy của mình rất đơn giản: Lấy dữ liệu về thì dùng GET, còn đẩy dữ liệu lên, sửa, xóa gì thì cứ nã POST tất. URL của mình ngày đó trông như một bãi chiến trường:
- /get_users.php
- /create_user.php
- /update_user.php?id=1
- /delete_user.php?id=1
Mọi thứ vẫn chạy tốt, cho đến khi mình làm việc trong một team lớn hơn, frontend và backend tách biệt (Mobile, React, Vue). Mấy anh em Frontend nhìn đống API của mình lắc đầu ngao ngán vì nó chả có một quy chuẩn nào cả. Và thế là mình bị bắt phải học về RESTful API và cách kết hợp các HTTP Methods.
1. Cuộc cách mạng mang tên RESTful: "Ngữ nghĩa hóa" API
RESTful không phải là một ngôn ngữ hay framework, nó là một bộ quy chuẩn thiết kế. Cốt lõi của nó là coi mọi thứ trên hệ thống của bạn là các Tài nguyên (Resources) (ví dụ: Users, Orders, Products), và sử dụng chính các HTTP Methods (GET, POST, PUT, PATCH, DELETE) để định nghĩa hành động (CRUD) lên tài nguyên đó.
Sự kết hợp này giúp URL của bạn trở nên vô cùng sạch sẽ. Thay vì viết các động từ vào URL như ngày xưa, bây giờ URL chỉ chứa Danh từ, còn Động từ đã được giao cho HTTP Method đảm nhận.
Dưới đây là "kim chỉ nam" cho anh em:
- GET (Read): Lấy dữ liệu. Không bao giờ được dùng GET để thay đổi trạng thái của database.
- POST (Create): Tạo mới một tài nguyên.
- PUT (Update - Thay thế toàn bộ): Cập nhật tài nguyên. Lưu ý, PUT mang ý nghĩa là "Replace" (Ghi đè). Nếu bạn PUT lên một User chỉ có mỗi trường name, nó có thể xóa trắng các trường age, email cũ của User đó (tùy logic code).
- PATCH (Update - Thay thế một phần): Cập nhật tài nguyên nhưng chỉ sửa những trường được gửi lên.
- DELETE (Delete): Xóa tài nguyên.
2. Sự vi diệu của Web Routing: Một đường dẫn, bốn ngã rẽ
Khi bạn kết hợp chuẩn RESTful với các hệ thống Web Routing hiện đại (trong Laravel, Express.js hay Golang Gin), bạn sẽ thấy một sự thanh lịch đến bất ngờ.
Cùng một URL là /users/1, nhưng tùy vào "thái độ" (HTTP Method) mà client gửi lên, Router của Backend sẽ bẻ lái nó vào các function khác nhau:
// Ví dụ với Express.js (Node.js)
const router = express.Router();
// 1. GET /users/1 -> Lấy thông tin user số 1
router.get('/users/:id', userController.getUser);
// 2. PUT /users/1 -> Cập nhật toàn bộ thông tin user số 1
router.put('/users/:id', userController.replaceUser);
// 3. PATCH /users/1 -> Cập nhật một vài trường của user số 1
router.patch('/users/:id', userController.updateUser);
// 4. DELETE /users/1 -> Xóa user số 1
router.delete('/users/:id', userController.deleteUser);
Chỉ nhìn vào Routing file, một developer mới vào team cũng có thể hiểu ngay hệ thống này đang làm gì với tài nguyên Users mà không cần đọc document dài dòng.
3. Những "cú vấp ngã" trên đường đời thực tế
Lý thuyết thì đẹp, nhưng thực tế đi làm luôn có những rào cản khiến anh em phải "bẻ luật". Dưới đây là những kinh nghiệm xương máu của mình:
Cạm bẫy 1: Form HTML truyền thống không hiểu PUT/DELETE
Nếu bạn code frontend thuần bằng thẻ <form> của HTML, bạn sẽ phát hiện ra nó chỉ hỗ trợ duy nhất 2 method là GET và POST.
Làm sao để gửi request DELETE để xóa user bây giờ?
Các framework như Laravel đã đẻ ra một trick rất hay gọi là Method Spoofing (Giả mạo method). Bạn vẫn submit form bằng POST, nhưng nhét thêm một thẻ input ẩn: <input type="hidden" name="_method" value="DELETE">. Router của Laravel khi đọc được field này sẽ tự động "hô biến" cái POST đó thành DELETE để xử lý đúng chuẩn RESTful.
Cạm bẫy 2: Tính lũy đẳng (Idempotency) - Sát thủ giấu mặt
Đây là kiến thức phân loại level của Backend Dev.
-
Các method GET, PUT, DELETE được thiết kế để có tính lũy đẳng: Tức là client có lỡ gửi request 1 lần hay 100 lần liên tiếp, thì kết quả cuối cùng trên server vẫn y hệt nhau (xóa 1 user 100 lần thì user đó vẫn bay màu, update tên 100 lần bằng PUT thì tên vẫn thế).
-
Nhưng POST thì KHÔNG lũy đẳng. Bạn gửi POST /orders 100 lần, hệ thống sẽ đẻ ra 100 cái đơn hàng mới.
Vì vậy, nếu dùng sai method (ví dụ dùng GET để tạo đơn hàng), khi user bị lag mạng và bấm F5 liên tục, database của bạn sẽ ngập lụt trong rác.
Lời kết
Việc thiết kế Routing và sử dụng HTTP Methods đúng chuẩn ban đầu có thể hơi gò bó, nhất là với anh em quen hệ "tự do". Nhưng tin mình đi, khi dự án phình to, khi phải viết API cho bên thứ 3 tích hợp, hoặc khi debug một cục code của người cũ để lại, bạn sẽ thầm cảm ơn vì ngày xưa cả team đã thống nhất dùng RESTful.
Sự kết hợp giữa HTTP Methods và Routing không chỉ là kỹ thuật, nó là tiếng nói chung, là ngôn ngữ giao tiếp để các developer (cả Front lẫn Back) có thể hiểu nhau mà không cần giải thích nhiều. Mọi thứ cứ theo chuẩn mà phang thôi!
All rights reserved