request.body、request.POST、request.read() 都在读同一个流,先读谁、读没读过,结果大不相同。这篇把 3 种内容类型 × 4 种读取顺序全部跑一遍,再看大小限制。
[urlencoded] 先 POST 再 body: POST={'a': ['1'], 'b': ['2']} | body=b'a=1&b=2'
[urlencoded] 先 body 再 POST: body=b'a=1&b=2' | POST={'a': ['1'], 'b': ['2']}
[urlencoded] 先 read 再 body: read()=b'a=1&b' | body → RawPostDataException: You cannot access body after reading from request's data stream
[urlencoded] 先 read 再 POST: read()=b'a=1&b' | POST={}
[multipart ] 先 POST 再 body: POST={'a': ['1']} | body → RawPostDataException: You cannot access body after reading from request's data stream
[multipart ] 先 body 再 POST: body=b'--BoUnDaRyStRiNg\r\nContent-Disposition: f' | POST={'a': ['1']}
[multipart ] 先 read 再 body: read()=b'--BoU' | body → RawPostDataException: You cannot access body after reading from request's data stream
[multipart ] 先 read 再 POST: read()=b'--BoU' | POST={}
[json ] 先 POST 再 body: POST={} | body=b'{"a": 1}'
[json ] 先 body 再 POST: body=b'{"a": 1}' | POST={}
[json ] 先 read 再 body: read()=b'{"a":' | body → RawPostDataException: You cannot access body after reading from request's data stream
[json ] 先 read 再 POST: read()=b'{"a":' | POST={}规律:request.body 会把整个体读进内存并缓存;POST 的解析在 urlencoded 时基于缓存的体,在 multipart 时直接消耗流。所以 multipart 的「先 POST 再 body」失败。一旦手动 read() 过,流就被消耗了:body 明确报错,而 POST 静默变空,后者更危险。
POST request.POST = {'a': ['1'], 'b': ['2']} request.body = b'a=1&b=2'
PUT request.POST = {} request.body = b'a=1&b=2'
PATCH request.POST = {} request.body = b'a=1&b=2'Django 只对 POST 方法解析表单体。REST 风格的 PUT / PATCH 要自己解析 request.body(或用 DRF 之类的框架)。
DATA_UPLOAD_MAX_MEMORY_SIZE 默认 = 2621440 字节(2.50 MiB) JSON/二进制体 2621440 字节 → status 200 body 长度 2621440 JSON/二进制体 2621441 字节 → status 400 RequestDataTooBig: Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE. multipart 里的文件 2.5MiB+1000 字节 → status 400 RequestDataTooBig: Request body exceeded settings.DATA_UPLOA
DATA_UPLOAD_MAX_MEMORY_SIZE 限制「读进内存」的请求体:读取 request.body 时,体超过限制就抛 RequestDataTooBig(探测视图里我把它转成了 400)。multipart 请求整体超过限制时读 body 也一样;而 multipart 解析出的文件字段走另一套上传处理器与阈值(第 68、69 篇),这次没有单独测 request.POST / request.FILES 遇到大文件时的行为。
读取前 _stream 类型: LimitedStream 是否已有 _body: False
读取 body 之后 是否有 _body: True 第二次 r.body is r.body: True
body 之后 read(): b'{"a": 1}'
body 之后 _stream 类型: BytesIO读取 body 后 _stream 被换成 BytesIO,所以之后还能 read();反过来则不行。