1. 概覽與概念
- Content-Type:HTTP 請求/響應頭,表示消息體的媒體類型(MIME type)。服務端用它決定如何解析請求體。
- 常見場景:
- 純結構化數據(JSON) →
application/json
- 表單 + 文件上傳 →
multipart/form-data
- 簡單表單鍵值對(HTML 表單默認)→
application/x-www-form-urlencoded
- 純文本 →
text/plain
- 原始二進制文件流 →
application/octet-stream
- 純結構化數據(JSON) →
選擇策略:如果只傳結構化數據(沒有文件),優先 application/json
;如果需要上傳文件,使用 multipart/form-data
。
2. application/json
定義
請求體為 JSON 文本。服務器按 JSON 解析整個請求體。
何時使用
- 只傳結構化數據(對象 / 數組 / 嵌套結構),不含文件。
- 常見于 RESTful API、微服務間通信、前端與后端交互。
請求頭
Content-Type: application/json; charset=utf-8
客戶端示例
curl
curl -X POST "http://example.com/api" \-H "Authorization: Bearer TOKEN" \-H "Content-Type: application/json" \-d '{"indexing_technique":"high_quality","process_rule":{"mode":"custom"}}'
Python (requests)
import requests
url = "http://example.com/api"
headers = {"Authorization": "Bearer TOKEN"}
data = {"indexing_technique": "high_quality", "process_rule": {"mode": "custom"}}
resp = requests.post(url, headers=headers, json=data) # 使用 json 參數,requests 會自動序列化并設置 Content-Type
Apifox/Postman:Body → 選擇 raw → JSON,填入 JSON。
服務端解析
多數框架能自動解析 JSON:例如 Flask (request.json
) / FastAPI(聲明模型)等。
注意點
- 必須是合法 JSON(不能有單引號、不允許在外層多包一層字符串)。
- 如果要上傳文件,不能用
application/json
(文件會被二進制編碼成 base64,但那不是推薦方式,且會增大尺寸)。
3. multipart/form-data
定義
表單分段(multipart),每個部分(part)都有自己的 headers(Content-Disposition
、可選 Content-Type
),適合混合文本字段與文件字段。
何時使用
- 需要上傳文件(圖片、文檔、音頻等)
- 同時需要傳復雜 JSON(把 JSON 放在一個 text 字段里)和文件
請求頭(示例)
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX
boundary
是庫自動生成,用于分隔各個 part。
客戶端示例
curl
curl -X POST "http://example.com/upload" \-H "Authorization: Bearer TOKEN" \-F 'data={"indexing_technique":"high_quality"};type=text/plain' \-F 'file=@/path/to/file.png'
注意:不要在
-F 'data="{...}"'
外層再加額外的引號,這會讓服務器把 data 當成字符串,造成解析失敗(例如你遇到的indexing_technique=None
問題)。
Python (requests) — 推薦寫法:
import requests, json
url = "http://example.com/upload"
headers = {"Authorization": "Bearer TOKEN"}
json_payload = {"indexing_technique": "high_quality"}
files = {# 文本字段: (filename, content, content_type) -> filename 為 None 或空"data": (None, json.dumps(json_payload), "text/plain"),# 文件字段"file": ("file.png", open("/path/to/file.png", "rb"))
}
resp = requests.post(url, headers=headers, files=files)
Apifox/Postman:Body → form-data,添加:
- key=
data
type=Text,值填{"indexing_technique":"high_quality"}
(不要額外的雙引號) - key=
file
type=File,選擇文件
服務端解析
- 在 Flask:使用
request.form
(文本字段)與request.files
(文件字段) - 在 FastAPI:使用
UploadFile
與Form()
來接收
示例(FastAPI)
from fastapi import FastAPI, File, UploadFile, Form
import json
app = FastAPI()@app.post('/upload')
async def upload(data: str = Form(...), file: UploadFile = File(...)):# data 是表單里的字符串,通常存放 JSON,需要 json.loadspayload = json.loads(data)content = await file.read()return {"received": payload, "filename": file.filename}
常見坑
- 把 JSON 用外層雙引號包裹(整個 JSON 當作一個字符串)會被解析成字符串,導致字段缺失或類型錯誤。
- 指定了錯誤的 Content-Type(例如把
data
的 Content-Type 寫成application/json
)在某些庫下會影響解析方式——requests
的files
會給 part 自動附 content-type。
4. application/x-www-form-urlencoded
定義
表單鍵值對序列化為 key1=value1&key2=value2
,類似 HTML 表單的默認提交方式,不支持文件上傳。
何時使用
- 簡單表單提交(登錄、查詢參數)
- 瀏覽器表單提交(不含文件)
客戶端示例
curl
curl -X POST "http://example.com/login" \-H "Content-Type: application/x-www-form-urlencoded" \-d "username=alice&password=secret"
Python (requests)
requests.post(url, data={"username": "alice", "password": "secret"})
Apifox/Postman:Body → x-www-form-urlencoded,按 key/value 填寫。
服務端解析
- Flask:
request.form['username']
- FastAPI:使用
Form()
來接收
5. text/plain
與 application/octet-stream
text/plain
- 用于傳輸簡單純文本(非 JSON 結構),如傳一段日志、一段腳本、或問題描述。
- 示例:
curl -X POST -H "Content-Type: text/plain" --data "hello world" http://...
application/octet-stream
-
二進制流,適用于直接上傳文件的原始字節流(例如大文件或非表單上傳的情形)。
-
示例:將文件直接作為請求體,而不是 multipart:
curl -X PUT "http://example.com/upload/raw" \-H "Content-Type: application/octet-stream" \--data-binary @/path/to/bigfile.bin
-
服務器端通常把整個請求體當成字節流讀取并保存。
6. 服務端如何接收(示例)
FastAPI(包括 JSON、multipart、raw)
from fastapi import FastAPI, File, UploadFile, Form, Request
import json
app = FastAPI()@app.post('/json')
async def recv_json(payload: dict):# 當 Content-Type: application/json 且 body 是 JSON,FastAPI 會自動解析并傳入 dictreturn {"ok": True, "payload": payload}@app.post('/upload-multipart')
async def upload_multipart(data: str = Form(...), file: UploadFile = File(...)):payload = json.loads(data)contents = await file.read()return {"ok": True, "name": file.filename, "payload": payload}@app.put('/upload-raw')
async def upload_raw(request: Request):# 適合 application/octet-streambody = await request.body()# 保存到文件示例with open('/tmp/out.bin', 'wb') as f:f.write(body)return {"ok": True, "size": len(body)}
Flask(簡潔示例)
from flask import Flask, request, jsonify
import json
app = Flask(__name__)@app.route('/json', methods=['POST'])
def json_route():payload = request.get_json(force=True)return jsonify(ok=True, payload=payload)@app.route('/upload', methods=['POST'])
def upload():data = request.form.get('data')payload = json.loads(data)file = request.files['file']file.save('/tmp/' + file.filename)return jsonify(ok=True)@app.route('/raw', methods=['PUT'])
def raw():body = request.data # bytesopen('/tmp/out.bin', 'wb').write(body)return jsonify(ok=True, size=len(body))
7. 常見錯誤、坑與排查建議
-
外層多加引號導致 JSON 變成字符串
- 問題表現:服務端解析后字段為
None
或整個字段是一個字符串。 - 排查:打印收到的原始 body(或查看 request.form),看是否是
"{...}"
。
- 問題表現:服務端解析后字段為
-
錯誤的 Content-Type
- 例如把 multipart 的 part 標為
application/json
,或把整個請求標為text/plain
。 - 排查:抓包(瀏覽器 DevTools / tcpdump / ngrok / mitmproxy)或在服務端記錄
request.headers
。
- 例如把 multipart 的 part 標為
-
字符編碼問題(中文 / Emoji)
- 保證
charset=utf-8
,并在客戶端使用 UTF-8 編碼發送。
- 保證
-
大文件上傳失敗或超時
- 原因:服務器限制(Nginx client_max_body_size、框架上傳限制)、超時。
- 解決:增大限制、分片上傳、直接使用云存儲的分片接口(S3 multipart upload)等。
-
boundary 被破壞
- 當手動拼接 multipart 而 boundary 未正確設置或被轉義時會失敗。建議使用客戶端庫(requests、curl、Postman)自動處理。
-
證書/代理/跨域問題
- POST 跨域:瀏覽器會發 OPTIONS 預檢,請確保服務器允許 CORS 并處理 Content-Type。服務器需返回合適的
Access-Control-Allow-Headers
。
- POST 跨域:瀏覽器會發 OPTIONS 預檢,請確保服務器允許 CORS 并處理 Content-Type。服務器需返回合適的
8. 性能、安全與兼容性建議
- 壓縮:對于較大的 JSON 或文本,可啟用
gzip
壓縮(Content-Encoding: gzip
),但客戶端/服務器需支持。注意:Content-Type
與Content-Encoding
是不同維度。 - 范圍/分片上傳:對于大文件,優先使用分片上傳(客戶端 + 服務端/云端支持),避免單次上傳失敗帶來的重試成本。
- 限速與大小控制:服務端應在網關或 Nginx 上配置大小限制、請求超時和速率限制。
- 校驗:文件上傳建議校驗
Content-Type
(魔數/文件頭)與文件大小,并在可能的場景下進行病毒掃描。 - 認證:在上傳接口強制認證與授權,避免未授權的大流量上傳。