Tài liệu API nào cũng có ví dụ curl, và gần như mọi lần tích hợp đều bắt đầu bằng việc chuyển một ví dụ như vậy thành mã. Đó là việc máy móc nhưng có vài cái bẫy: tùy chọn có nghĩa không hiển nhiên, mặc định khác nhau giữa curl và thư viện HTTP của bạn, và một khóa API thật nằm lại trong lịch sử shell, trong kho mã hay trong cửa sổ chat. Bài này đi qua một ví dụ thực tế với công cụ chuyển đổi curl, rồi nói về các cái bẫy. Mọi khóa dưới đây đều là giả.
Ví dụ
Một ví dụ "tạo thanh toán" theo kiểu nhiều API thanh toán:
curl https://api.example-pay.com/v1/payment_intents \
-u "sk_test_FAKE123:" \
-d amount=2000 \
-d currency=usd \
-d "automatic_payment_methods[enabled]"=trueĐọc nó như curl đọc:
- URL là đối số duy nhất không phải tùy chọn.
- -u "sk_test_FAKE123:" là xác thực HTTP Basic với khóa làm tên người dùng và mật khẩu rỗng (đó là ý nghĩa của dấu hai chấm ở cuối; thiếu nó, curl sẽ hỏi mật khẩu).
- Ba tùy chọn -d được nối bằng & thành một nội dung: amount=2000¤cy=usd&automatic_payment_methods[enabled]=true.
- Có -d mà không có -X thì là POST, và curl thêm Content-Type: application/x-www-form-urlencoded.
Với Python requests, công cụ tạo ra:
import requests
url = "https://api.example-pay.com/v1/payment_intents"
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
data = {
"amount": "2000",
"currency": "usd",
"automatic_payment_methods[enabled]": "true",
}
response = requests.post(
url,
headers=headers,
data=data,
auth=("sk_test_FAKE123", ""),
allow_redirects=False,
)
print(response.status_code)
print(response.text)Mỗi dòng ứng với một phần của lệnh. allow_redirects=False có mặt vì curl không đi theo chuyển hướng nếu không có -L, còn requests thì mặc định có. Phần lớn API không chuyển hướng, nhưng khi có (ví dụ URL http chuyển sang https), khác biệt này hiện ra thành mã 301 khó hiểu ở công cụ này và thành công ở công cụ kia.
Những chi tiết làm đổi hành vi
- -d, --data-raw và --data-binary. -d coi @ ở đầu là "đọc tệp này" và bỏ ký tự xuống dòng trong tệp; --data-raw gửi @ nguyên văn; --data-binary gửi tệp đúng từng byte. Công cụ giữ tham chiếu tệp là tham chiếu tệp và báo cho bạn.
- --data-urlencode mã hóa giá trị (dấu cách thành +, é thành %C3%A9). "q=café & bar" thành q=caf%C3%A9+%26+bar. -d thường thì không mã hóa gì.
- -G chuyển toàn bộ dữ liệu vào chuỗi truy vấn và giữ phương thức GET: curl -G URL -d limit=10 gọi URL?limit=10.
- --json (curl 7.82 trở lên) là cách viết tắt cho nội dung JSON cùng header Content-Type và Accept. Với -d '{…}' thường, curl vẫn ghi application/x-www-form-urlencoded trừ khi bạn tự thêm header; nhiều ví dụ trong tài liệu quên điều này, và một số máy chủ từ chối nội dung.
- -k tắt kiểm tra chứng chỉ. Nó chỉ dành cho thử nghiệm với máy chủ cục bộ, không bao giờ cho mã chạy thật.
- Trình duyệt chặt hơn curl. fetch trong trình duyệt không đặt được Cookie, User-Agent hay Referer và chịu CORS. Mã chạy được trên Node có thể hỏng trên trình duyệt chỉ vì những lý do này.
Khóa trong lịch sử shell
Lệnh trên, gõ với khóa thật, giờ nằm trong ~/.bash_history hoặc ~/.zsh_history dưới dạng chữ thường, có thể còn được sao lưu. Các cách tránh:
- Dùng biến: đặt khóa một lần, đọc bằng read -s để không hiện ra màn hình, rồi dùng "$API_KEY" trong lệnh. Lịch sử khi đó chỉ chứa tên biến. Công cụ giữ $API_KEY nguyên dạng chữ và liệt kê nó, để mã của bạn cũng đọc từ biến môi trường.
- Không ghi lệnh vào lịch sử: trong bash, với HISTCONTROL=ignorespace (hoặc ignoreboth), lệnh bắt đầu bằng dấu cách không được lưu; trong zsh, setopt HIST_IGNORE_SPACE làm điều tương tự.
- Để header trong tệp: từ curl 7.55, -H @headers.txt đọc header từ tệp, tệp này có thể để ngoài hệ thống quản lý phiên bản.
- Nhớ danh sách tiến trình: khi curl đang chạy, đối số của nó hiện cho người dùng khác trên cùng máy qua ps. Trên máy chủ dùng chung, tệp an toàn hơn đối số.
Khóa trong mã và trong các cuộc trò chuyện
Mã được tạo rất dễ dán: vào kho mã, vào issue, vào chat với đồng nghiệp hay trợ lý. Công cụ đánh dấu các yêu cầu có header Authorization, mật khẩu -u, cookie hoặc tham số truy vấn giống khóa. Trước khi lưu mã:
- Thay khóa thật bằng việc đọc biến môi trường: os.environ["API_KEY"] trong Python, process.env.API_KEY trong Node, os.Getenv("API_KEY") trong Go.
- Đưa tệp chứa giá trị thật (.env, trình quản lý bí mật) vào .gitignore.
- Nếu khóa thật từng bị dán ở nơi công khai, hãy đổi khóa. Xóa tin nhắn hay commit không làm khóa hết lộ; có những công cụ tự động quét khóa trong các kho công khai.
Bản thân công cụ chuyển đổi không gửi lệnh đi đâu: lệnh được phân tích ngay trong trang, nên có thể dán lệnh chứa khóa thử nghiệm trong lúc làm việc.
Những lỗi thường gặp
- Quên mật khẩu rỗng trong -u "key:" rồi bị hỏi mật khẩu hoặc nhận 401.
- Gửi JSON bằng -d mà không có Content-Type, rồi thắc mắc vì sao máy chủ báo nội dung rỗng.
- Tưởng -d mã hóa giá trị. Chỉ --data-urlencode làm việc đó.
- Chép -k vào mã chạy thật vì "curl chạy được".
- Thử trên Node, triển khai trên trình duyệt, nơi header bị cấm bị bỏ âm thầm và CORS được áp dụng.
- Để khóa thật trong ví dụ ở README hay wiki của chính bạn. Hãy dùng giá trị giả rõ ràng như sk_test_FAKE123.
Khi yêu cầu đã chạy, công cụ định dạng JSON giúp đọc phản hồi dễ hơn.