Danh sách bài viết

Bài 2: HTTP Methods: GET, POST, PUT, PATCH, DELETE

Bài 2 của series Rust RESTful API — tổng quan 9 HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE, CONNECT) theo RFC 7231 và RFC 5789, phân biệt safe vs unsafe, idempotent vs non-idempotent, quy ước REST mapping method với CRUD trên Shop API, sự khác biệt PUT và PATCH, cùng method override pattern X-HTTP-Method-Override cho legacy client.

12/06/2026
10 phút đọc
2 lượt xem
1

Mục Tiêu Bài Học

Sau bài học, bạn sẽ:

  • Liệt kê được 9 HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE, CONNECT) và mục đích của từng method.
  • Phân biệt được safeunsafe method, hiểu vì sao server không được phép thay đổi state khi xử lý safe method.
  • Phân biệt được idempotentnon-idempotent, biết method nào an toàn để retry khi mạng lỗi.
  • Nắm quy ước REST mapping method với thao tác CRUD: POST=create, GET=read, PUT=replace, PATCH=update, DELETE=remove.
  • Biết khi nào dùng PUT, khi nào dùng PATCH, và cách hai method này thể hiện trong Shop API.
  • Hiểu method override pattern qua header X-HTTP-Method-Override và form field _method dành cho legacy client.
2

9 HTTP Method Toàn Cảnh

RFC 7231 (Semantics and Content của HTTP/1.1, năm 2014) định nghĩa 8 method cốt lõi: GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE. RFC 5789 (năm 2010) bổ sung PATCH như một extension cho partial update. Tổng cộng 9 method được dùng phổ biến trong REST API hiện đại.

Bảng overview ba thuộc tính quan trọng — safe (không đổi state), idempotent (gọi N lần giống 1 lần), và cacheable (response cache được):

Method    Safe   Idempotent   Cacheable    Mục đích
GET       Yes    Yes          Yes          Đọc resource
HEAD      Yes    Yes          Yes          Đọc metadata, không body
POST      No     No           Conditional  Tạo resource, trigger action
PUT       No     Yes          No           Thay thế toàn bộ resource
PATCH     No     No           Conditional  Sửa một phần resource
DELETE    No     Yes          No           Xoá resource
OPTIONS   Yes    Yes          No           Liệt kê method allowed
TRACE     Yes    Yes          No           Echo request (debug)
CONNECT   No     No           No           Tunneling cho HTTPS proxy

POST và PATCH được đánh dấu conditional cacheable: response chỉ cache được nếu server gửi rõ Cache-Control hoặc Expires cho phép, mặc định không cache. Bốn method GET, POST, PUT, DELETE chiếm gần như toàn bộ traffic REST thực tế; PATCH bổ sung cho update bộ phận. Năm method còn lại phục vụ vai trò chuyên biệt, sẽ trình bày ở mục 6.

3

Safe Method — Read-Only Không Side Effect

Một method được gọi là safe khi nó không thay đổi state của server. Cụ thể: gọi method đó một lần, mười lần, hay một triệu lần thì dữ liệu trên server vẫn nguyên vẹn. Theo RFC 7231 mục 4.2.1, bốn method safe là GET, HEAD, OPTIONS, TRACE.

Cụm từ "không thay đổi state" cần hiểu chính xác. Server hoàn toàn được phép ghi log access, tăng metric counter, hay cập nhật cache nội bộ khi nhận GET — đó là side effect quan sát chứ không phải side effect do client yêu cầu. Quy tắc thật sự: client không được phép kỳ vọng resource bị sửa đổi khi gọi safe method, và server không được phép phụ thuộc vào side effect đó như một phần contract.

Đặc tính safe có ý nghĩa thực tiễn rất lớn. Trình duyệt được phép prefetch link GET ngầm trước khi user click. Search engine crawler được phép GET mọi URL công khai mà không lo phá dữ liệu. Reverse proxy được phép cache response GET và phục vụ lại nhiều client. Toàn bộ hạ tầng web — CDN, browser cache, web crawler — vận hành được vì có ranh giới rõ ràng giữa safe và unsafe method.

Trong tư duy SQL, safe method tương đương SELECT — chỉ đọc, không bao giờ INSERT/UPDATE/DELETE. Một anti-pattern thường gặp là dùng GET cho endpoint kích hoạt action: GET /users/42/delete hay GET /orders/99/cancel. Endpoint kiểu này phá vỡ contract safe, khiến crawler vô tình xoá dữ liệu khi index site. Action phải dùng POST hoặc DELETE.

4

Idempotent Method — Gọi N Lần = Gọi 1 Lần

Một method được gọi là idempotent khi gọi nó N lần liên tiếp dẫn tới cùng một state cuối trên server như khi gọi 1 lần. Theo RFC 7231 mục 4.2.2, sáu method idempotent là GET, HEAD, OPTIONS, TRACE, PUT, DELETE.

Lưu ý: idempotent không có nghĩa response giống nhau. Gọi DELETE /products/42 lần đầu trả 204 No Content (xoá thành công); lần hai trả 404 Not Found (đã xoá rồi). Response khác nhau, nhưng state server sau lần một và sau lần hai đều giống nhau — product 42 không tồn tại. Đó vẫn là idempotent.

POST không idempotent: mỗi POST đến cùng endpoint tạo một resource mới. Gọi POST /api/v1/products hai lần sinh ra hai product khác nhau với hai ID khác nhau. Đây là khác biệt cốt lõi với PUT.

PATCH thường không idempotent: vì payload PATCH có thể là delta tương đối (ví dụ {"stock_increment": 1}). Tuy nhiên PATCH với payload tuyệt đối ({"stock": 100}) thì idempotent. RFC 5789 nói rõ: PATCH có thể idempotent hoặc không, tuỳ ngữ nghĩa server định nghĩa.

Ứng dụng quan trọng nhất của idempotency là retry-on-failure. Khi client gửi PUT mà nhận timeout, client không biết server đã nhận và xử lý hay chưa. Vì PUT idempotent, client retry an toàn — kết quả cuối vẫn đúng. Với POST, retry mù sinh duplicate; phải dùng kỹ thuật idempotency key (sẽ học ở bài payment) để chống duplicate. Đây là một trong những lý do tại sao Stripe, GitHub, AWS đều yêu cầu idempotency key cho POST.

5

GET, POST, PUT, PATCH, DELETE — Chi Tiết

Năm method dưới đây chiếm hơn 99% traffic REST API thực tế.

GET — Đọc Resource

Lấy biểu diễn của resource. Tham số đi qua query string: ?page=1&size=20&sort=price:asc. Theo RFC 7231 mục 8.1.3, GET không nên có body — nhiều proxy và CDN sẽ strip body GET. Response cacheable theo header Cache-ControlETag. Mã trả về thường gặp: 200 OK, 304 Not Modified (cache hit), 404 Not Found.

GET /api/v1/products/42 HTTP/1.1
Host: shop.example.com
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
ETag: "a1b2c3d4"
Cache-Control: public, max-age=300

{"id": 42, "name": "Laptop X", "price": 1299.00}

POST — Tạo Resource Hoặc Trigger Action

Tạo resource mới hoặc kích hoạt một action server-side (ví dụ POST /api/v1/checkout). Body chứa dữ liệu, content-type thường là application/json. Khi tạo resource thành công, server trả 201 Created kèm header Location chỉ tới URL resource vừa tạo. POST không idempotent.

POST /api/v1/products HTTP/1.1
Host: shop.example.com
Content-Type: application/json

{"name": "Laptop Y", "price": 1499.00, "stock": 10}

HTTP/1.1 201 Created
Location: /api/v1/products/43
Content-Type: application/json

{"id": 43, "name": "Laptop Y", "price": 1499.00, "stock": 10}

PUT — Thay Thế Toàn Bộ Resource

Body PUT là biểu diễn đầy đủ của resource sau khi update. Server thay toàn bộ resource bằng body; field nào không có trong body coi như bị reset về default. PUT idempotent: gọi N lần cùng body cho kết quả giống nhau. Mã trả về: 200 OK (kèm resource mới) hoặc 204 No Content (không body).

PUT /api/v1/products/43 HTTP/1.1
Host: shop.example.com
Content-Type: application/json

{"name": "Laptop Y Pro", "price": 1799.00, "stock": 5}

HTTP/1.1 200 OK

PATCH — Update Một Phần

Body PATCH chứa chỉ thay đổi cần áp dụng, không phải toàn bộ resource. Hai chuẩn payload phổ biến: JSON Patch (RFC 6902) dạng array operation [{"op": "replace", "path": "/price", "value": 1599}], và JSON Merge Patch (RFC 7396) đơn giản hơn dạng object {"price": 1599}. Phần lớn API public chọn Merge Patch vì gọn.

PATCH /api/v1/products/43 HTTP/1.1
Host: shop.example.com
Content-Type: application/merge-patch+json

{"price": 1599.00}

HTTP/1.1 200 OK

DELETE — Xoá Resource

Xoá resource khỏi server. DELETE idempotent: lần đầu xoá trả 204 No Content, các lần sau trả 404 Not Found nhưng state server sau mỗi lần đều giống nhau (resource không tồn tại). Mã trả về thường gặp: 200 OK (kèm resource vừa xoá), 204 No Content, 404 Not Found.

DELETE /api/v1/products/43 HTTP/1.1
Host: shop.example.com
Authorization: Bearer eyJhbGc...

HTTP/1.1 204 No Content
6

HEAD, OPTIONS, TRACE, CONNECT — Less Common

Bốn method còn lại ít gặp trong handler ứng dụng nhưng vẫn xuất hiện hằng ngày ở tầng hạ tầng.

HEAD — Như GET Nhưng Không Body

HEAD trả về header giống hệt GET nhưng không kèm body. Dùng để check nhanh metadata: kích thước qua Content-Length, thời điểm sửa đổi qua Last-Modified, version qua ETag. Trình quản lý download dùng HEAD để xác định kích thước file trước khi tải; cache layer dùng HEAD để invalidate khi ETag đổi.

OPTIONS — Liệt Kê Method Allowed

Trả về header Allow liệt kê method được phép trên một resource. Ứng dụng quan trọng nhất là CORS preflight: trước khi gửi request cross-origin có header tuỳ chỉnh hoặc method khác GET/POST/HEAD, browser tự động gửi một OPTIONS request hỏi server có cho phép hay không.

OPTIONS /api/v1/products/43 HTTP/1.1
Host: shop.example.com
Origin: https://app.example.com
Access-Control-Request-Method: PATCH

HTTP/1.1 204 No Content
Allow: GET, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, PUT, PATCH, DELETE

TRACE — Debug Echo

Server echo lại request mà client vừa gửi để client thấy chính xác phiên bản đã đi qua mọi proxy trung gian. Hữu ích cho debug, nhưng tiềm ẩn rủi ro lộ header nhạy cảm (Cookie, Authorization) — gọi là Cross-Site Tracing. Thực tế phần lớn server production disable TRACE bằng cách trả 405 Method Not Allowed.

CONNECT — Tunneling Cho HTTPS Proxy

Client yêu cầu proxy thiết lập một TCP tunnel tới host đích, sau đó dữ liệu TLS truyền raw qua tunnel. Đây là cách HTTPS đi qua proxy HTTP truyền thống. Application backend gần như không bao giờ tự xử lý CONNECT — đó là việc của proxy/gateway.

7

Quy Ước REST: Method ↔ CRUD ↔ URL

Quy ước mapping HTTP method với thao tác CRUD và URL pattern là viên gạch chính của uniform interface đã giới thiệu ở bài 1. Bảng dưới áp dụng trực tiếp cho Shop API, dùng resource products làm ví dụ:

CRUD       HTTP     URL Pattern                Response thành công
Create     POST     /api/v1/products           201 Created + Location header
Read all   GET      /api/v1/products           200 OK + danh sách (paginated)
Read one   GET      /api/v1/products/:id       200 OK + entity / 404 Not Found
Update     PUT      /api/v1/products/:id       200 OK / 204 No Content
Partial    PATCH    /api/v1/products/:id       200 OK + entity sau update
Delete     DELETE   /api/v1/products/:id       204 No Content / 404 Not Found

Quy ước này lặp lại cho mọi resource trong Shop API — orders, cart/items, categories, reviews, ... tất cả đều theo cùng pattern /api/v1/<resource> và cùng mapping method. Lợi ích: developer mới nhìn vào endpoint đoán được hành vi không cần đọc doc. Đó chính là tinh thần uniform mà Fielding đề xuất.

Vài lưu ý áp dụng vào Shop:

  • List có filter: GET /api/v1/products?category=laptop&min_price=500. Tham số đi qua query string, không phải body.
  • Sub-resource: GET /api/v1/products/:slug/reviews đọc reviews thuộc product cụ thể.
  • Action không map gọn CRUD: dùng POST với verb-noun rõ ràng, ví dụ POST /api/v1/orders/:id/cancel, POST /api/v1/checkout. Không lạm dụng RPC-style nhưng cũng không cố gò mọi thứ vào CRUD.
  • Admin scope: tách prefix /api/v1/admin/<resource> để phân quyền tầng route, ví dụ POST /api/v1/admin/products.
8

Method Override Pattern

Một số môi trường client cũ chỉ hỗ trợ GET và POST: HTML form thuần (chỉ method="get"method="post"), một số firewall hoặc proxy cũ chặn PUT/PATCH/DELETE, một vài SDK enterprise legacy. Khi cần PUT/PATCH/DELETE qua những client này, dùng method override pattern.

Hai cách phổ biến:

  • Header override: client gửi POST kèm header X-HTTP-Method-Override: PUT. Server phát hiện header và xử lý request như PUT thật.
  • Form field override: client gửi POST với form field _method=DELETE. Server đọc field và xử lý như DELETE.
POST /api/v1/products/43 HTTP/1.1
Host: shop.example.com
X-HTTP-Method-Override: PATCH
Content-Type: application/json

{"price": 1599.00}

Server phải explicitly enable tính năng này. Trong axum, không có middleware override sẵn — bạn cần tự viết một tower::Layer nhỏ inspect header trước khi route matching, hoặc dùng pattern tương đương từ tower-http. Để mặc định không bật là quyết định bảo mật hợp lý: nếu mọi POST đều có thể trở thành DELETE, CSRF mitigation và rate limit dựa trên method sẽ vô hiệu.

Trong API hiện đại, method override gần như không cần thiết. Mọi browser hỗ trợ fetch() với mọi method; mọi mobile SDK (Retrofit, Alamofire, ...) đều dùng PUT/PATCH/DELETE trực tiếp; HTTP client phía Rust như reqwest không có giới hạn nào. Shop API sẽ không bật override — chỉ giữ kiến thức này để hiểu code base legacy nếu gặp khi maintain.

9

Tổng Kết

  • 9 HTTP method dùng phổ biến: 8 method từ RFC 7231 (GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE) cộng PATCH từ RFC 5789.
  • Safe = read-only, server không đổi state do client yêu cầu: GET, HEAD, OPTIONS, TRACE. Cho phép prefetch, crawler index, cache vô tư.
  • Idempotent = gọi N lần dẫn tới state cuối giống 1 lần: GET, HEAD, OPTIONS, TRACE, PUT, DELETE. Cho phép retry an toàn khi mạng lỗi.
  • POST không idempotent — mỗi POST sinh resource mới. PATCH thường không idempotent vì có thể chứa delta tương đối.
  • REST mapping: POST=create, GET=read, PUT=replace toàn bộ, PATCH=update một phần, DELETE=remove. Apply nhất quán cho mọi resource Shop API qua pattern /api/v1/<resource>.
  • PUT yêu cầu body đầy đủ; PATCH chỉ chứa thay đổi. Shop API sẽ dùng cả hai ở B65/B66.
  • HEAD cho check metadata mà không tải body. OPTIONS phục vụ CORS preflight và liệt kê method allowed.
  • TRACE và CONNECT hiếm gặp ở handler ứng dụng — TRACE thường disable vì rủi ro security, CONNECT là việc của proxy.
  • Method override (X-HTTP-Method-Override, _method) là workaround cho legacy client; API modern không cần và Shop API sẽ không bật.
10

Bài Tập Củng Cố

Tự trả lời, đáp án ở cuối:

  1. Liệt kê các method được coi là safe theo RFC 7231. Vì sao GET được liệt kê safe trong khi server vẫn ghi access log mỗi lần GET?
  2. PUT có idempotent không? POST có idempotent không? Giải thích tại sao có khác biệt, và hệ quả của khác biệt đó khi retry request thất bại.
  3. Khi tạo product mới trong Shop API, nên dùng HTTP method nào? Endpoint URL? Status code nào server trả về khi tạo thành công, và header nào cần đính kèm?
  4. Phân biệt PUT và PATCH ở ba khía cạnh: nội dung body, tính idempotent, và content-type thường dùng. Khi nào nên chọn cái nào?
  5. OPTIONS method có vai trò gì trong CORS? Trình duyệt gửi OPTIONS preflight trong tình huống cụ thể nào?
Đáp án
  1. Safe method theo RFC 7231 mục 4.2.1: GET, HEAD, OPTIONS, TRACE. GET vẫn safe dù server ghi log vì "safe" có nghĩa client không kỳ vọng resource bị sửa đổi và server không phụ thuộc vào side effect đó như một phần contract. Access log, metric, internal cache update là side effect quan sát chứ không phải state thay đổi do client yêu cầu. Quy tắc là crawler/prefetcher gọi GET bất kỳ lúc nào cũng không phá dữ liệu.
  2. PUT idempotent, POST không idempotent. PUT vì body chứa biểu diễn đầy đủ resource — gọi PUT cùng body N lần cho state cuối giống nhau. POST tạo resource mới mỗi lần — N POST sinh N resource khác nhau. Hệ quả khi retry: PUT timeout retry được an toàn vì kết quả cuối vẫn đúng. POST timeout retry mù sinh duplicate; phải dùng idempotency key (client gửi UUID kèm request, server lưu key cùng response, request thứ hai cùng key trả response cũ không xử lý lại).
  3. Method POST, endpoint POST /api/v1/products (hoặc POST /api/v1/admin/products nếu chỉ admin được tạo). Status code 201 Created, header Location: /api/v1/products/<id_mới> trỏ tới URL của product vừa tạo. Body response thường kèm representation đầy đủ của product (id, name, price, stock, timestamp).
  4. PUT body chứa biểu diễn đầy đủ của resource (toàn bộ field); PATCH body chỉ chứa thay đổi cần áp dụng (subset field). PUT idempotent; PATCH thường không idempotent. Content-type PUT thường application/json; PATCH thường application/merge-patch+json (RFC 7396) hoặc application/json-patch+json (RFC 6902). Chọn PUT khi client có sẵn full state và muốn thay toàn bộ; chọn PATCH khi chỉ update vài field, tiết kiệm băng thông và tránh ghi đè field không liên quan từ client khác.
  5. OPTIONS phục vụ CORS preflight: trước khi gửi request cross-origin có method khác GET/POST/HEAD hoặc có header tuỳ chỉnh (vd Authorization, Content-Type: application/json với body không phải form), browser tự động gửi OPTIONS hỏi server có cho phép request thực sắp tới hay không. Response chứa header Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers. Nếu OPTIONS pass, browser mới gửi request thực; nếu fail, browser block và không gọi handler.
11

Bài Tiếp Theo

— giới thiệu 5 class status code, top 20 code thường gặp (200, 201, 204, 301, 304, 400, 401, 403, 404, 409, 422, 429, 500, 502, 503, 504), cách chọn đúng code cho mỗi tình huống, anti-pattern 200-with-error-body.