Skip to content

2.4 - HTTP/HTTPS ​

Tóm tắt: HTTP là một giao thức văn bản đơn giản tới mức gõ tay được. Nhưng ba chỗ trong nó thường xuyên bị dùng sai. Method có ngữ nghĩa: GET phải an toàn và PUT/DELETE phải idempotent — vi phạm thì proxy, trình duyệt và cơ chế retry sẽ làm hỏng dữ liệu của bạn. Status code nói với máy, không nói với người: trả 200 kèm {"error": ...} là làm mù mọi lớp giám sát. Và giá trị header chỉ được chứa ASCII — một dấu tiếng Việt lọt vào là HttpClient ném ngoại lệ trước khi request kịp gửi đi.

Mục tiêu bài học ​

Sau bài này bạn có thể:

  • Đọc và tự gõ một request HTTP thô.
  • Giải thích "an toàn" và "idempotent", và nói được method nào có tính chất nào.
  • Chọn đúng status code, đặc biệt phân biệt 401 với 403, và 400 với 409.
  • Nêu giới hạn ký tự của giá trị header và cách gửi tiếng Việt cho đúng.
  • Chỉ ra HTTPS bảo vệ được gì và không bảo vệ được gì.

Cần biết trước: 2.3 — Client - Server Model, và API thực hành CrmLab ở mục 2.3.6 đang chạy (dùng cho Bài 3). Bài 2 cần tạo được project console (bài 0.2).

Nội dung bài học ​

2.4.1 — Một request trông như thế nào ​

http
POST /api/customers HTTP/1.1
Host: api.company.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
Accept-Language: vi-VN

{"name":"Nguyễn Văn A","email":"[email protected]"}

Bốn phần: dòng đầu (method, đường dẫn, phiên bản), các header, một dòng trống, rồi phần thân. Response cũng đúng cấu trúc đó, chỉ khác dòng đầu:

http
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/customers/42

{"id":42,"name":"Nguyễn Văn A"}

Dòng trống là bắt buộc — nó báo cho bên nhận biết header đã hết.

2.4.2 — Method có ngữ nghĩa, không chỉ là cái tên ​

MethodAn toànIdempotentNghĩa
GETcócóĐọc, không đổi gì
HEADcócóNhư GET nhưng không lấy thân
POSTkhôngkhôngTạo mới, hoặc thao tác không xếp được vào đâu
PUTkhôngcóThay thế toàn bộ tài nguyên
PATCHkhôngkhôngSửa một phần
DELETEkhôngcóXoá

Hai khái niệm này không phải lý thuyết suông:

  • An toàn nghĩa là không làm đổi trạng thái. Trình duyệt, proxy và trình thu thập dữ liệu tự ý gọi GET bất cứ lúc nào. Nếu bạn làm GET /orders/42/delete thì một con bot quét link sẽ xoá sạch đơn hàng. Đây là chuyện đã xảy ra với nhiều hệ thống thật.
  • Idempotent nghĩa là gọi một lần hay mười lần cho cùng kết quả. Quan trọng vì mạng không đáng tin: client gửi request, mất phản hồi, và gửi lại. Với DELETE thì vô hại. Với POST tạo đơn hàng thì bạn có hai đơn.

Cách xử lý POST bị gửi lại: yêu cầu client gửi kèm một khoá idempotency và từ chối khoá đã dùng.

http
POST /api/orders
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7

2.4.3 — Status code: nói với máy, không nói với người ​

Ranh giới 4xx/5xx quyết định ai phải đi sửa: 4xx là "bạn gửi sai", 5xx là "tôi hỏng". Đặt sai nhóm là đổ lỗi nhầm người, và làm hỏng mọi cảnh báo dựng trên tỉ lệ lỗi 5xx.

Những mã hay dùng nhất:

MãKhi nào
200 OKThành công, có thân phản hồi
201 CreatedĐã tạo — kèm header Location
204 No ContentThành công, không có gì để trả
301 / 308Chuyển hướng vĩnh viễn
302 / 307Chuyển hướng tạm thời
304 Not ModifiedClient dùng bản cache đi
400 Bad RequestRequest dị dạng, hoặc dữ liệu không hợp lệ (thiếu trường, sai định dạng)
401 UnauthorizedChưa biết bạn là ai
403 ForbiddenBiết bạn là ai, nhưng không cho
404 Not FoundKhông có tài nguyên này
409 ConflictXung đột trạng thái, ví dụ email đã tồn tại
422 Unprocessable ContentTuỳ chọn: một số API dùng cho "cú pháp đúng, dữ liệu sai"
429 Too Many RequestsVượt giới hạn tần suất
500 Internal Server ErrorLỗi không lường trước
502 / 503 / 504Gateway lỗi / quá tải / hết giờ chờ

Hai cặp hay nhầm nhất:

  • 401 và 403. 401 nghĩa là "hãy đăng nhập đi" — thiếu hoặc hỏng token. 403 nghĩa là "đã đăng nhập rồi nhưng không đủ quyền". Trả 401 cho một người đã đăng nhập khiến frontend đá họ về trang login một cách vô lý.
  • 400 và 409. 400 là request sai: JSON hỏng, thiếu trường bắt buộc, age = -5. 409 là request đúng nhưng xung đột với trạng thái hiện tại: email đã có người dùng, đơn hàng đã bị huỷ trước đó. Khoá học này theo quy ước của ASP.NET Core: lỗi kiểm tra dữ liệu trả 400 (mặc định của [ApiController], bài 8.5), trùng hoặc xung đột trả 409. Một số API khác dùng 422 cho lỗi dữ liệu; chọn kiểu nào cũng được, miễn ghi rõ trong tài liệu và dùng nhất quán.

Và một lỗi nghiêm trọng hơn cả hai: trả 200 kèm thân báo lỗi.

json
HTTP/1.1 200 OK
{"success": false, "message": "Khách hàng không tồn tại"}

Mọi thứ nằm ngoài code của bạn — dashboard giám sát, cân bằng tải, cơ chế retry, CDN — đều chỉ nhìn status code. Với chúng, response trên là một thành công. Tỉ lệ lỗi của hệ thống sẽ mãi mãi hiển thị 0%.

Danh sách đầy đủ: HTTP response status codes — MDN.

2.4.4 — Header, và giới hạn ASCII ít ai biết ​

http
Content-Type: application/json; charset=utf-8
Authorization: Bearer <token>
Cache-Control: no-store
X-Request-Id: 7c9e6679-7425-40de-944b

Đây là chỗ có một cái bẫy thật sự. Theo RFC 9110, giá trị header là chuỗi ký tự US-ASCII in được. Byte ngoài khoảng đó (obs-text) tồn tại trong đặc tả vì lý do lịch sử, nhưng không có cách diễn giải chuẩn.

Hệ quả trong .NET: gán một chuỗi có dấu tiếng Việt vào header thì Headers.Add vẫn chạy bình thường, nhưng HttpClient ném ngoại lệ lúc gửi, trước khi request được gửi đi.

csharp
request.Headers.Add("X-City", "Hà Nội");     // KHÔNG ném ở đây
await client.SendAsync(request);               // ném HttpRequestException ở đây:
// "Request headers must contain only ASCII characters."

Cách đúng là mã hoá theo RFC 8187, hoặc chuyển dữ liệu xuống phần thân:

http
X-City*: UTF-8''H%C3%A0%20N%E1%BB%99i

Ba bài đi sâu vào đúng chuyện này, từ ba góc khác nhau:

2.4.5 — Cache: ETag và Cache-Control ​

http
# Lần đầu
GET /api/customers/42
→ 200 OK
  ETag: "v3"
  Cache-Control: max-age=60

# Lần sau
GET /api/customers/42
If-None-Match: "v3"
→ 304 Not Modified          ← không có thân, tiết kiệm băng thông

ETag là dấu vân tay của nội dung. Client gửi lại dấu đó, máy chủ so sánh, và nếu chưa đổi thì trả 304 rỗng.

ETag còn dùng để chống ghi đè lẫn nhau:

http
PUT /api/customers/42
If-Match: "v3"
→ 412 Precondition Failed    ← ai đó đã sửa trước bạn

2.4.6 — HTTPS bảo vệ gì, và không bảo vệ gì ​

Có: nội dung request và response, đường dẫn URL, query string, header, cookie. Người ở giữa chỉ thấy một luồng byte vô nghĩa.

Không: tên miền bạn truy cập (lộ qua SNI và DNS), địa chỉ IP, kích thước và thời điểm các gói tin. Và quan trọng nhất — HTTPS không kiểm tra máy chủ có đáng tin về mặt nội dung hay không; nó chỉ chứng minh máy chủ đúng là chủ của tên miền đó. Một trang lừa đảo vẫn có ổ khoá xanh.

Thêm một lớp nữa nên bật: Strict-Transport-Security buộc trình duyệt chỉ dùng HTTPS cho tên miền này, kể cả khi người dùng gõ http://.

http
Strict-Transport-Security: max-age=31536000; includeSubDomains

2.4.7 — Danh sách rà soát cho dự án thật ​

Danh sách rà soát HTTP

  • Không có endpoint GET nào làm thay đổi dữ liệu.
  • Lỗi luôn trả mã 4xx hoặc 5xx, không bao giờ trả 200 kèm thân báo lỗi.
  • Phân biệt đúng 401 (chưa đăng nhập) và 403 (không đủ quyền).
  • Endpoint tạo mới trả 201 kèm header Location.
  • Không có giá trị header nào chứa ký tự ngoài ASCII.
  • POST tạo tài nguyên có cơ chế idempotency-key để chống gửi lại.
  • Đã bật Strict-Transport-Security trên tên miền production.

Bài tập áp dụng ​

Bài 1 — Gõ HTTP bằng tay ​

Dùng curl -v gọi một trang HTTPS bất kỳ, ví dụ https://tiennhm.io.vn/, và đọc toàn bộ request lẫn response thô. Chỉ ra bốn phần trong mỗi bên. Sau đó thử curl -I và giải thích khác biệt.

Tiêu chí hoàn thành: bạn chỉ đúng bốn phần trên output thật, và nói được vì sao HEAD tồn tại.

Gợi ý và lời giải — Bài 1

Gợi ý. Trong output của curl -v, dấu > đánh dấu dòng gửi đi, dấu < đánh dấu dòng nhận về, và dấu * là ghi chú của chính curl chứ không thuộc giao thức.

Lời giải.

bash
curl -v https://tiennhm.io.vn/ -o /dev/null

Bốn phần của request:

text
> GET / HTTP/2              <- 1. Dòng khởi đầu: method, đường dẫn, phiên bản
> Host: tiennhm.io.vn       <- 2. Header
> user-agent: curl/8.5.0
> accept: */*
>                           <- 3. Dòng trống, ngăn header với thân
                            <- 4. Thân — GET không có thân

Bốn phần của response:

text
< HTTP/2 200                <- 1. Dòng trạng thái: phiên bản, mã, lý do
< content-type: text/html   <- 2. Header
< content-length: 48213
<                           <- 3. Dòng trống
<!DOCTYPE html>...          <- 4. Thân

Về HEAD. Lưu ý curl -X HEAD thường bị treo vì curl vẫn chờ thân phản hồi; cách đúng là dùng curl -I:

bash
curl -I https://tiennhm.io.vn/

HEAD trả về y hệt header của GET nhưng không có thân. Nó tồn tại cho ba việc thực tế:

  1. Kiểm tra tài nguyên có tồn tại không mà không tải nội dung — hữu ích với file lớn.
  2. Đọc Content-Length để biết trước kích thước trước khi quyết định tải.
  3. Kiểm tra ETag hoặc Last-Modified xem bản đệm còn dùng được không.

Chi tiết đáng chú ý. Header content-length trong phản hồi HEAD báo kích thước mà GET sẽ trả về, dù bản thân HEAD không gửi byte nội dung nào.

Với ASP.NET Core thì sao? Endpoint khai báo bằng MapGet chỉ nhận GET. Thử curl -I http://localhost:5000/ vào CrmLab, nơi / khai báo bằng MapGet. Kết quả chạy thật trên .NET 10:

text
HTTP/1.1 405 Method Not Allowed

Muốn endpoint trả lời cả HEAD, khai báo rõ hai method:

csharp
app.MapMethods("/hello", ["GET", "HEAD"], () => "Xin chào");

Với HEAD, Kestrel (web server của ASP.NET Core) tự bỏ phần thân và chỉ gửi header.

Thử thêm. Chạy curl -v --http1.1 và so với mặc định. Với HTTP/2, curl hiển thị tên header bằng chữ thường vì giao thức yêu cầu vậy, còn HTTP/1.1 giữ nguyên chữ hoa thường như được gửi. Tên header không phân biệt hoa thường ở cả hai phiên bản, nên code so sánh tên header phải dùng so sánh không phân biệt hoa thường.

Bài 2 — Tự tạo lỗi ASCII trong header ​

Viết một đoạn C# thêm header có giá trị "Hà Nội" rồi gửi bằng HttpClient. Ghi lại ngoại lệ đầy đủ. Sau đó mã hoá theo RFC 8187 và xác nhận request đi được.

Tiêu chí hoàn thành: bạn ghi lại được chính xác chỗ ngoại lệ được ném ra — và nó không phải chỗ bạn đoán.

Gợi ý và lời giải — Bài 2

Gợi ý. Đặt điểm dừng (bài 0.3) ở cả dòng Headers.Add lẫn dòng SendAsync. Một trong hai dòng ném ngoại lệ, và việc nó là dòng nào mới là điều đáng học ở bài này.

Lời giải. Để CrmLab (mục 2.3.6) chạy ở cổng 5000, tạo một project console khác rồi thay Program.cs:

csharp
using var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");

try
{
    req.Headers.Add("X-City", "Hà Nội");
    Console.WriteLine("Thêm header: OK (không ném lúc Add)");
    var res = await client.SendAsync(req);
    Console.WriteLine($"Gửi được: {(int)res.StatusCode}");
}
catch (Exception ex)
{
    Console.WriteLine($"NÉM: {ex.GetType().Name}");
    Console.WriteLine($"     {ex.Message}");
}

Kết quả chạy thật trên .NET 10:

text
Thêm header: OK (không ném lúc Add)
NÉM: HttpRequestException
     Request headers must contain only ASCII characters.

Đây là điểm quan trọng nhất của bài. Headers.Add không kiểm tra ký tự, nên đoạn code trông như chạy bình thường. Ngoại lệ chỉ xuất hiện lúc gửi, tức là:

  • Bài kiểm thử đơn vị nào chỉ dựng HttpRequestMessage rồi kiểm tra header sẽ pass.
  • Lỗi chỉ lộ ra khi thật sự gọi mạng, tức là ở môi trường tích hợp hoặc production.
  • Thông báo lỗi không nói header nào sai, nên với request có hai chục header thì phải tự đi tìm.

Cách sửa theo RFC 8187. Một HttpRequestMessage chỉ gửi được một lần, nên tạo request mới:

csharp
var enc = Uri.EscapeDataString("Hà Nội");
var req2 = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
req2.Headers.Add("X-City", $"UTF-8''{enc}");
Console.WriteLine($"Giá trị RFC 8187: UTF-8''{enc}");
var res2 = await client.SendAsync(req2);
Console.WriteLine($"Gửi sau khi mã hoá: {(int)res2.StatusCode}");

Kết quả chạy thật:

text
Giá trị RFC 8187: UTF-8''H%C3%A0%20N%E1%BB%99i
Gửi sau khi mã hoá: 200

Định dạng gồm ba phần: tên bảng mã, một phần ngôn ngữ để trống giữa hai dấu nháy đơn, rồi giá trị đã mã hoá phần trăm. Bên nhận giải mã ngược lại.

Vì sao giới hạn này tồn tại. Đặc tả HTTP/1.1 quy định header dùng bảng mã ASCII. Ký tự ngoài ASCII có thể bị các proxy và máy chủ trung gian diễn giải khác nhau, dẫn tới một lớp tấn công gọi là chèn header. .NET chọn cách chặn thẳng từ phía client thay vì để hành vi trở nên khó đoán.

Ba chỗ hay dính lỗi này trong thực tế:

ChỗVí dụ giá trị
Tên file trong Content-Dispositionbáo-cáo-quý-4.xlsx
Header tuỳ biến mang tên người dùngNguyễn Văn An
Thông báo lỗi nhét vào headerKhông tìm thấy khách hàng

Trường hợp thứ nhất phổ biến nhất và có cách xử lý riêng: Content-Disposition hỗ trợ tham số filename* theo đúng RFC 8187, và ASP.NET Core tự làm việc này khi bạn dùng File(stream, contentType, fileName). Chi tiết ở bài 9.5.

Bài Một dấu tiếng Việt làm chết lời gọi API là một sự cố production có thật bắt đầu từ đúng lỗi này.

Bài 3 — Soát lại mã trạng thái ​

Với CrmLab đang chạy (mục 2.3.6), gọi sáu tình huống thất bại dưới đây và ghi lại mã trạng thái. Chỉ ra chỗ nào trả mã sai, và chỗ nào trả đúng.

  1. Đăng nhập sai mật khẩu.
  2. Đăng nhập với JSON hỏng, ví dụ {"email":.
  3. Gọi GET /api/customers không kèm token.
  4. Gọi GET /api/customers/999, một id không tồn tại.
  5. Gọi GET /api/customers/search?name=xyz, không có khách nào khớp.
  6. Tài khoản Sales gọi DELETE /api/customers/101.

Tiêu chí hoàn thành: với mỗi chỗ sai, bạn nêu được hậu quả cụ thể chứ không chỉ nói là không đúng chuẩn.

Gợi ý và lời giải — Bài 3

Gợi ý. Đừng bắt đầu từ việc tra bảng mã trạng thái. Bắt đầu từ hai câu hỏi:

  1. Ai đã làm sai? — client gửi sai thì 4xx, server hỏng thì 5xx.
  2. Gửi lại y hệt request này có ích gì không? — không thì 4xx, có thể thì 5xx.

Lời giải. Lấy TOKEN của [email protected] như bước 4 ở mục 2.3.6, rồi chạy:

bash
B=http://localhost:5000
f() { label=$1; shift; echo "$label -> $(curl -s -o /dev/null -w "%{http_code}" "$@")"; }

f "1. Đăng nhập sai mật khẩu" -X POST $B/auth/login \
  -H "Content-Type: application/json" -d '{"email":"[email protected]","password":"sai"}'
f "2. Đăng nhập JSON hỏng" -X POST $B/auth/login \
  -H "Content-Type: application/json" -d '{"email":'
f "3. Danh sách, không token" $B/api/customers
f "4. Khách hàng 999" $B/api/customers/999 -H "Authorization: Bearer $TOKEN"
f "5. Tìm 'xyz'" "$B/api/customers/search?name=xyz" -H "Authorization: Bearer $TOKEN"
f "6. Sales xoá khách hàng" -X DELETE $B/api/customers/101 -H "Authorization: Bearer $TOKEN"

Kết quả chạy thật:

text
1. Đăng nhập sai mật khẩu -> 401
2. Đăng nhập JSON hỏng -> 400
3. Danh sách, không token -> 401
4. Khách hàng 999 -> 404
5. Tìm 'xyz' -> 200
6. Sales xoá khách hàng -> 403

Bốn mã đầu (dòng 1–4) và mã cuối (dòng 6) đều đúng. Dòng 5 là chỗ sai: thân phản hồi là {"success":false,"message":"Khong tim thay khach hang"} đi kèm 200, tức báo lỗi trong thân nhưng mã trạng thái nói thành công. Với một endpoint tìm kiếm, "không có kết quả" thật ra không phải lỗi: cách đúng là trả 200 kèm danh sách rỗng []. Còn khi lấy một bản ghi theo id mà không có, đúng là 404 như dòng 4.

Để ý thêm dòng 2: CrmLab chạy ở môi trường Development nên thân của lỗi 400 chứa cả tên ngoại lệ BadHttpRequestException. Ở môi trường Production, ASP.NET Core không đưa chi tiết đó ra ngoài.

Bảng soát tổng quát cho những tình huống hay gặp:

Tình huốngMã đúngMã hay bị dùng saiHậu quả cụ thể của cái sai
Thiếu token hoặc token hết hạn401403Client không biết nên làm mới token, người dùng bị đăng xuất vô cớ
Có token nhưng không đủ quyền403401Client cố làm mới token vô hạn, tạo vòng lặp gọi API
Không tìm thấy bản ghi404200 + success: falseBộ đệm lưu lại phản hồi lỗi; hệ thống giám sát không đếm được
Dữ liệu gửi lên không hợp lệ400500Cảnh báo giả lúc nửa đêm; client tự động thử lại vô ích
Xung đột trạng thái, ví dụ email trùng409400Client không phân biệt được lỗi nhập liệu với lỗi trùng dữ liệu
Server gặp lỗi không lường trước500200 + success: falseSự cố không được phát hiện vì mọi thứ trông như thành công

Vì sao 200 kèm success: false là lựa chọn tệ nhất. Nó phá vỡ mọi tầng hạ tầng bên ngoài code của bạn, vì tất cả đều đọc mã trạng thái chứ không đọc thân phản hồi:

  • Hệ thống giám sát báo tỉ lệ lỗi bằng 0 trong khi hệ thống đang hỏng.
  • Bộ cân bằng tải không loại máy chủ hỏng ra khỏi vòng, vì nó vẫn trả 200.
  • Bộ đệm và CDN lưu lại phản hồi lỗi và phục vụ nó cho những người dùng khác.
  • Logic thử lại của client không kích hoạt, vì 200 nghĩa là thành công.

Vì sao nhầm 401 với 403 gây vòng lặp. Client thường được viết theo quy tắc: gặp 401 thì gọi endpoint làm mới token rồi thử lại. Nếu server trả 401 cho trường hợp thiếu quyền, client làm mới token thành công, thử lại, lại nhận 401, lại làm mới — vòng lặp vô hạn, tạo tải đáng kể lên hệ thống xác thực.

Cách chuẩn hoá. Dùng định dạng ProblemDetails theo RFC 9457 cho mọi phản hồi lỗi, với status khớp đúng mã HTTP:

json
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.4",
  "title": "Không đủ quyền",
  "status": 403,
  "detail": "Tài khoản của bạn không có quyền xoá khách hàng.",
  "instance": "/api/v1/customers/42",
  "traceId": "00-4bf92f...-01"
}

Trường traceId là mã tương quan để tra log — thứ biến câu "tôi gặp lỗi" thành một truy vấn log chính xác. Bài 8.8 trình bày cách cấu hình để mọi ngoại lệ tự động trả về định dạng này.

Tự kiểm tra ​

An toàn và idempotent khác nhau thế nào?

An toàn nghĩa là không làm thay đổi trạng thái, như GET và HEAD — nên trình duyệt và bot tự ý gọi lúc nào cũng được. Idempotent nghĩa là gọi một lần hay nhiều lần đều cho cùng kết quả, như PUT và DELETE — quan trọng vì mạng không đáng tin và client hay gửi lại. POST không có cả hai tính chất, nên tạo tài nguyên bằng POST cần khoá idempotency để chống trùng.

Vì sao không được để GET làm thay đổi dữ liệu?

Vì GET được đặc tả là an toàn, nên trình duyệt, proxy, trình thu thập dữ liệu và trình duyệt tăng tốc đều tự ý gọi nó. Một endpoint như GET /orders/42/delete sẽ bị một con bot quét link kích hoạt và xoá dữ liệu mà không ai bấm nút nào.

401 và 403 khác nhau ở đâu?

401 Unauthorized nghĩa là máy chủ chưa biết bạn là ai — thiếu token, token hỏng hoặc hết hạn, và cách xử lý là đăng nhập. 403 Forbidden nghĩa là đã biết bạn là ai nhưng tài khoản này không đủ quyền, đăng nhập lại cũng không giúp gì. Trả nhầm 401 cho người đã đăng nhập khiến frontend đá họ về trang login một cách vô lý.

Vì sao trả 200 kèm success:false lại tệ?

Vì mọi thứ nằm ngoài code của bạn — dashboard giám sát, cân bằng tải, cơ chế retry, CDN — chỉ nhìn status code chứ không đọc thân phản hồi. Với chúng, response đó là thành công, nên tỉ lệ lỗi của hệ thống sẽ mãi hiển thị 0% dù người dùng đang gặp lỗi liên tục.

Giá trị header HTTP chứa được tiếng Việt không?

Không. Theo RFC 9110, giá trị header là chuỗi ký tự US-ASCII in được. Byte ngoài khoảng đó tồn tại trong đặc tả vì lý do lịch sử nhưng không có cách diễn giải chuẩn. Trong .NET, Headers.Add vẫn nhận chuỗi có dấu, nhưng HttpClient ném HttpRequestException lúc SendAsync, trước khi request kịp gửi đi. Cách đúng là mã hoá theo RFC 8187 hoặc chuyển dữ liệu xuống phần thân.

HTTPS không bảo vệ được những gì?

Không giấu được tên miền bạn truy cập, vì nó lộ qua SNI và qua truy vấn DNS. Không giấu địa chỉ IP, kích thước và thời điểm các gói tin. Và nó không nói gì về việc máy chủ có đáng tin về nội dung hay không — chứng chỉ chỉ chứng minh máy chủ là chủ của tên miền, nên một trang lừa đảo vẫn có ổ khoá xanh.

Kết luận ​

Ba điều đáng nhớ nhất:

  1. Method là một lời hứa. GET hứa không đổi gì, và cả Internet tin lời hứa đó.
  2. Status code là cách nói chuyện với máy móc. Trả 200 cho một lỗi là tự làm mù hệ thống giám sát của chính mình.
  3. Header chỉ chứa ASCII. Một dấu tiếng Việt lọt vào là đủ để giết một lời gọi API trước khi nó kịp gửi đi.

Tham khảo ​

Điều hướng ​

Bài liên quan ​

Phát hành theo giấy phép MIT.