如何在Django Rest Framework中处理带gzip压缩体的POST请求
解决方案:DRF处理gzip压缩的JSON请求体
首先纠正前端代码的错误:你的前端代码中,axios.post的请求体传的是原始的data,而非压缩后的gz_data,这会导致后端根本没收到压缩数据,先修正这一点:
async postData(json_data) { let headers = authHeader(); headers['Content-Encoding'] = 'gzip'; headers['Content-Type'] = 'application/json'; const data = json_data; const gz_data = pako.gzip(JSON.stringify(data)); return axios .post(API_URL + 'create/', gz_data, // 这里改成gz_data,不是原始data { headers: headers, } ) .then(response => { return response.data; }) }
接下来解决后端的解压问题,提供两种可行方案:
方案一:自定义DRF解析器(视图级控制,推荐)
继承DRF的JSONParser,添加gzip解压逻辑,仅在需要的视图中使用该解析器:
import zlib import io from rest_framework.parsers import JSONParser from rest_framework.exceptions import ParseError class GzipJSONParser(JSONParser): def parse(self, stream, media_type=None, parser_context=None): parser_context = parser_context or {} request = parser_context.get('request') encoding = request.headers.get('Content-Encoding', '') # 仅处理gzip编码的请求 if 'gzip' in encoding.lower(): try: # 读取压缩数据并解压 compressed_data = stream.read() # 限制解压后大小,防止gzip炸弹(示例限制为10MB) decompressed_data = zlib.decompress(compressed_data, zlib.MAX_WBITS | 16) if len(decompressed_data) > 10 * 1024 * 1024: raise ParseError("Decompressed data exceeds size limit") # 将解压后的数据转为类文件对象,交给父类解析JSON stream = io.BytesIO(decompressed_data) except zlib.error as e: raise ParseError(f"Failed to decompress gzip data: {str(e)}") # 调用父类的parse方法解析JSON return super().parse(stream, media_type, parser_context)
然后在你的视图中指定使用这个解析器:
from rest_framework import generics class DataList(generics.ListCreateAPIView): serializer_class = DataSerializer authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] parser_classes = [GzipJSONParser] # 添加这一行指定解析器 def get_queryset(self): return Data.objects.filter(user=self.request.user) def post(self, request, format=None): transect_data = request.data # 现在可以正常获取解析后的JSON数据 # 后续业务逻辑...
方案二:Django中间件(全局生效)
如果需要全局处理所有gzip压缩的JSON请求,可以编写一个中间件,在请求到达DRF之前解压请求体:
import zlib from django.http import BadRequest class GzipRequestMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): encoding = request.headers.get('Content-Encoding', '') content_type = request.headers.get('Content-Type', '') # 仅处理Content-Type为application/json且Content-Encoding包含gzip的请求 if 'gzip' in encoding.lower() and 'application/json' in content_type.lower(): try: # 读取压缩数据 compressed_data = request.body # 解压并限制大小(示例限制为10MB) decompressed_data = zlib.decompress(compressed_data, zlib.MAX_WBITS | 16) if len(decompressed_data) > 10 * 1024 * 1024: return BadRequest("Decompressed data exceeds size limit") # 替换request的body为解压后的数据 request._body = decompressed_data # 更新Content-Encoding,避免后续中间件重复处理 updated_encoding = request.META['HTTP_CONTENT_ENCODING'].replace('gzip', '').strip() if updated_encoding: request.META['HTTP_CONTENT_ENCODING'] = updated_encoding else: del request.META['HTTP_CONTENT_ENCODING'] except zlib.error: return BadRequest("Invalid gzip compressed data") response = self.get_response(request) return response
然后在settings.py的MIDDLEWARE列表中添加这个中间件(注意要放在DRF相关中间件之前):
MIDDLEWARE = [ # ... 其他中间件 'your_app.middleware.GzipRequestMiddleware', # 替换为你的中间件实际路径 'django.contrib.auth.middleware.AuthenticationMiddleware', 'rest_framework.authentication.SessionAuthentication', # ... ]
安全注意事项
- 强制大小限制:必须限制解压后的数据大小,避免恶意构造的gzip炸弹耗尽服务器资源
- 严格校验请求头:只处理
Content-Type为application/json且Content-Encoding包含gzip的请求 - 仅信任可信客户端:确保只有你的前端应用发送此类压缩请求,避免未知来源的恶意请求
内容的提问来源于stack exchange,提问作者Beinje
相关产品推荐
相关产品推荐

