curl 深度教程 / 第 04 章:HTTP 方法详解
第 04 章:HTTP 方法详解
HTTP 方法定义了客户端对资源的"意图"。正确理解和使用 HTTP 方法,是构建和调试 RESTful API 的基础。
4.1 HTTP 方法概览
HTTP/1.1 规范(RFC 9110)定义了以下常用方法:
| 方法 | 语义 | 幂等 | 安全 | 请求体 | 常见用途 |
|---|---|---|---|---|---|
| GET | 获取资源 | ✅ | ✅ | ❌ | 查询数据 |
| HEAD | 获取资源元数据 | ✅ | ✅ | ❌ | 检查资源是否存在、获取头信息 |
| POST | 创建资源/提交数据 | ❌ | ❌ | ✅ | 创建新资源、提交表单 |
| PUT | 替换整个资源 | ✅ | ❌ | ✅ | 更新完整资源 |
| PATCH | 部分更新资源 | ❌ | ❌ | ✅ | 更新部分字段 |
| DELETE | 删除资源 | ✅ | ❌ | 可选 | 删除资源 |
| OPTIONS | 查询支持的方法 | ✅ | ✅ | ❌ | CORS 预检请求 |
| TRACE | 回显请求 | ✅ | ✅ | ❌ | 诊断(通常被禁用) |
| CONNECT | 建立隧道 | ❌ | ❌ | ❌ | HTTPS 代理 |
幂等(Idempotent):多次请求结果相同。GET/PUT/DELETE 是幂等的,POST 不是。 安全(Safe):不修改服务器状态。GET/HEAD/OPTIONS/TRACE 是安全的。
4.2 GET — 获取资源
GET 是最常用的 HTTP 方法,用于从服务器获取资源。
基本 GET
# 最简单的 GET
curl https://httpbin.org/get
# 带查询参数
curl "https://httpbin.org/get?page=1&limit=20&sort=date"
# 使用 -G 和 --data-urlencode 自动编码参数
curl -G https://httpbin.org/get \
--data-urlencode "q=hello world" \
--data-urlencode "lang=zh"
# 设置 Accept 头,指定返回格式
curl -H "Accept: application/json" https://api.example.com/users
curl -H "Accept: text/html" https://api.example.com/users
curl -H "Accept: application/xml" https://api.example.com/users
GET 常见业务场景
# 分页查询
curl "https://api.example.com/users?page=2&per_page=50"
# 过滤查询
curl "https://api.example.com/orders?status=paid&date_from=2024-01-01"
# 搜索
curl -G https://api.example.com/search \
--data-urlencode "q=机器学习" \
--data-urlencode "category=技术"
# 获取特定资源
curl https://api.example.com/users/42
# 获取关联资源
curl https://api.example.com/users/42/orders
# 缓存控制:使用 If-None-Match 条件请求
curl -H "If-None-Match: \"abc123\"" https://api.example.com/data
GET 的注意事项
# ❌ 错误:GET 不应该有请求体(虽然 curl 允许,但不符合语义)
curl -X GET https://api.example.com/users -d '{"filter": "active"}'
# ✅ 正确:将参数放在 URL 中
curl -G https://api.example.com/users \
--data-urlencode "filter=active"
# ❌ 错误:不要用 GET 执行修改操作
curl -X GET https://api.example.com/users/42/delete
# ✅ 正确:使用 DELETE 方法
curl -X DELETE https://api.example.com/users/42
4.3 HEAD — 获取元数据
HEAD 方法与 GET 类似,但服务器只返回响应头,不返回响应体。
基本 HEAD
# 获取响应头(-I 等价于 -X HEAD 并隐藏响应体)
curl -I https://example.com
# 输出示例:
# HTTP/2 200
# content-type: text/html; charset=UTF-8
# content-length: 1256
# date: Sat, 10 May 2026 12:00:00 GMT
# etag: "abc123"
# last-modified: Fri, 09 May 2026 10:00:00 GMT
# 使用 -X HEAD(效果类似,但会显示下载进度)
curl -X HEAD -s -o /dev/null -w "%{http_code}" https://example.com
HEAD 的业务场景
# 检查资源是否存在
status=$(curl -s -o /dev/null -w "%{http_code}" -I https://api.example.com/users/42)
if [ "$status" = "200" ]; then
echo "用户存在"
elif [ "$status" = "404" ]; then
echo "用户不存在"
fi
# 检查文件大小(不下载)
curl -sI https://example.com/largefile.zip | grep -i content-length
# 输出:content-length: 104857600
# 检查文件类型
curl -sI https://example.com/download | grep -i content-type
# 输出:content-type: application/pdf
# 检查是否需要认证
curl -s -o /dev/null -w "%{http_code}" -I https://api.example.com/private
# 401 → 需要认证
# 检查 CORS 头
curl -sI -H "Origin: https://myapp.com" https://api.example.com/data \
| grep -i "access-control"
# 获取 Last-Modified 用于条件下载
curl -sI https://example.com/file.tar.gz | grep -i last-modified
4.4 POST — 创建/提交数据
POST 是语义最丰富的方法,用于创建资源、提交表单、上传文件等。
基本 POST
# 发送表单数据(application/x-www-form-urlencoded)
curl -X POST https://httpbin.org/post \
-d "username=admin&password=secret123"
# 发送 JSON 数据
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "secret123"}'
# 从文件发送 JSON
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d @payload.json
# 发送原始文本
curl -X POST https://httpbin.org/post \
-H "Content-Type: text/plain" \
-d "Hello, World!"
# 发送 XML
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><user><name>张三</name></user>'
POST 的业务场景
# 创建用户
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"name": "张三",
"email": "[email protected]",
"role": "developer"
}'
# 提交订单
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{
"product_id": 42,
"quantity": 2,
"shipping_address": "北京市朝阳区"
}'
# 文件上传(multipart/form-data)
curl -X POST https://api.example.com/upload \
-F "file=@/path/to/document.pdf" \
-F "description=季度报告"
# 触发 Webhook
curl -X POST https://hooks.example.com/deploy \
-H "Content-Type: application/json" \
-H "X-Hook-Secret: my-secret" \
-d '{"event": "push", "ref": "main"}'
# 发送邮件(SMTP)
curl --url "smtp://smtp.example.com:587" \
--ssl-reqd \
--mail-from "[email protected]" \
--mail-rcpt "[email protected]" \
--user "[email protected]:password" \
-T email.txt
4.5 PUT — 完整替换资源
PUT 用于替换目标资源的全部内容。PUT 是幂等的——多次执行结果相同。
基本 PUT
# 更新整个资源
curl -X PUT https://api.example.com/users/42 \
-H "Content-Type: application/json" \
-d '{
"name": "张三(已更新)",
"email": "[email protected]",
"role": "senior-developer",
"department": "工程部"
}'
# 替换配置文件
curl -X PUT https://api.example.com/config/web \
-H "Content-Type: application/json" \
-d @config.json
# 上传文件到指定位置(S3 风格)
curl -X PUT https://storage.example.com/bucket/file.txt \
-H "Content-Type: text/plain" \
-d "这是文件的新内容"
PUT vs POST 的区别
# POST:创建新资源(服务器分配 ID)
# 每次执行可能创建不同的资源
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "张三"}'
# 响应:201 Created, Location: /users/42
# PUT:替换指定 ID 的资源
# 如果不存在则创建,存在则更新
curl -X PUT https://api.example.com/users/42 \
-H "Content-Type: application/json" \
-d '{"name": "张三"}'
# 响应:200 OK 或 201 Created
4.6 PATCH — 部分更新
PATCH 用于对资源进行部分修改,只发送需要变更的字段。
基本 PATCH
# 仅更新邮箱字段
curl -X PATCH https://api.example.com/users/42 \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
# 更新多个字段
curl -X PATCH https://api.example.com/users/42 \
-H "Content-Type: application/json" \
-d '{
"role": "tech-lead",
"salary": 50000
}'
# 使用 JSON Merge Patch(RFC 7396)
curl -X PATCH https://api.example.com/settings \
-H "Content-Type: application/json" \
-d '{"theme": "dark"}'
# 使用 JSON Patch(RFC 6902)
curl -X PATCH https://api.example.com/users/42 \
-H "Content-Type: application/json-patch+json" \
-d '[
{"op": "replace", "path": "/name", "value": "李四"},
{"op": "add", "path": "/tags/-", "value": "vip"},
{"op": "remove", "path": "/temp_field"}
]'
PUT vs PATCH 对比
| 特性 | PUT | PATCH |
|---|---|---|
| 语义 | 完整替换 | 部分更新 |
| 幂等性 | ✅ 是 | ❌ 否(通常) |
| 请求体 | 完整资源 | 仅变更部分 |
| 缺失字段 | 会被删除/置空 | 保持不变 |
| 带宽 | 较大 | 较小 |
| Content-Type | application/json | application/json 或 application/json-patch+json |
4.7 DELETE — 删除资源
基本 DELETE
# 删除单个资源
curl -X DELETE https://api.example.com/users/42
# 响应:204 No Content
# 删除带认证保护的资源
curl -X DELETE https://api.example.com/users/42 \
-H "Authorization: Bearer $TOKEN"
# 批量删除(通过请求体)
curl -X DELETE https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"ids": [42, 43, 44]}'
# 软删除(通过 PATCH 设置 deleted 标记)
curl -X PATCH https://api.example.com/users/42 \
-H "Content-Type: application/json" \
-d '{"deleted": true, "deleted_at": "2026-05-10T12:00:00Z"}'
# 确认删除结果
curl -s -o /dev/null -w "%{http_code}" \
-X DELETE https://api.example.com/users/999
# 204 → 成功删除
# 404 → 资源不存在
4.8 OPTIONS — 查询支持的方法
OPTIONS 用于查询服务器支持哪些 HTTP 方法,也是 CORS 预检请求的机制。
基本 OPTIONS
# 查询服务器支持的方法
curl -X OPTIONS https://api.example.com/users -v
# 查看 Allow 头
curl -s -X OPTIONS -I https://api.example.com/users | grep -i allow
# 输出:Allow: GET, POST, PUT, DELETE, OPTIONS
# CORS 预检请求
curl -X OPTIONS https://api.example.com/data \
-H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization"
# 查看 CORS 响应头
curl -sI -X OPTIONS https://api.example.com/data \
-H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
| grep -i "access-control"
CORS 预检请求详解
# 完整的 CORS 预检流程
# 1. 浏览器先发送 OPTIONS 请求
curl -v -X OPTIONS https://api.example.com/data \
-H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: DELETE" \
-H "Access-Control-Request-Headers: Authorization, Content-Type"
# 2. 检查响应中的 CORS 头
# Access-Control-Allow-Origin: https://myapp.com
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE
# Access-Control-Allow-Headers: Authorization, Content-Type
# Access-Control-Max-Age: 86400
# 3. 如果预检通过,浏览器才会发送实际请求
curl -X DELETE https://api.example.com/users/42 \
-H "Origin: https://myapp.com" \
-H "Authorization: Bearer token"
4.9 数据格式
常见 Content-Type 对比
| Content-Type | 格式 | 适用场景 | curl 方式 |
|---|---|---|---|
application/x-www-form-urlencoded | key=value&key2=value2 | HTML 表单 | -d "key=value" |
multipart/form-data | 分段边界 | 文件上传 | -F "key=value" |
application/json | JSON | REST API | -d '{"key":"value"}' |
application/xml | XML | SOAP/旧系统 | -d '<key>value</key>' |
text/plain | 纯文本 | 日志/原始数据 | -d 'text' |
application/octet-stream | 二进制流 | 文件上传 | --data-binary @file |
application/grpc | gRPC 协议 | 微服务通信 | 见第 13 章 |
格式转换示例
# 发送 JSON,请求 XML 响应
curl -X POST https://api.example.com/data \
-H "Content-Type: application/json" \
-H "Accept: application/xml" \
-d '{"key": "value"}'
# 发送表单,请求 JSON 响应
curl -X POST https://api.example.com/data \
-H "Accept: application/json" \
-d "name=张三&age=30"
# 发送 CSV
curl -X POST https://api.example.com/import \
-H "Content-Type: text/csv" \
--data-binary @data.csv
注意事项
- GET 不要有 Body:虽然技术上可行,但违反语义且可能被代理丢弃
- POST 不是幂等的:重复提交 POST 可能创建重复资源,使用幂等键(Idempotency-Key)可解决
- PUT 要发送完整资源:不要只发送部分字段,缺失字段可能被清空
- DELETE 可能有 Body:虽然少见,但 RFC 允许 DELETE 携带请求体
- 安全考虑:生产环境通常禁用 TRACE 方法(防止跨站追踪攻击)
# 幂等键示例(防止重复创建)
curl -X POST https://api.example.com/payments \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"amount": 100, "currency": "CNY"}'
扩展阅读
- RFC 9110 — HTTP Semantics
- MDN — HTTP 请求方法
- RESTful API 设计指南
- JSON Patch (RFC 6902)
- JSON Merge Patch (RFC 7396)
📖 下一章:第 05 章:请求头与响应头 — 深入了解 HTTP 头部的管理,包括自定义头、Cookie 和 Content-Type。