读取request.body时Django挂起的解决方法(无法修改客户端)
解决Django读取Content-Length不匹配请求时的挂起问题
问题背景
客户端发送的请求中,Content-Length头被设置为13409,但实际请求体为空字符串。当这类请求到达Django 3.2.15的视图时,调用request.body会导致程序挂起——因为Django会持续等待读取到声明长度的请求体内容,且不会主动抛出异常。由于无法修改客户端,只能通过调整视图逻辑解决该问题。
解决方案
方法1:设置读取超时并校验长度
通过信号机制给请求体读取操作设置超时,避免无限等待;同时校验实际读取长度与声明的Content-Length是否匹配,提前拦截异常请求。
示例代码:
import signal from django.http import HttpResponse, HttpResponseBadRequest class TimeoutError(Exception): pass def timeout_handler(signum, frame): raise TimeoutError("读取请求体超时") def my_view(request): # 设置5秒超时,可根据业务需求调整 signal.signal(signal.SIGALRM, timeout_handler) signal.alarm(5) try: content = request.body # 校验长度匹配性 declared_length = request.META.get('CONTENT_LENGTH') if declared_length is not None and len(content) != int(declared_length): return HttpResponseBadRequest("请求体长度与声明不符") # 后续业务逻辑处理 return HttpResponse('<h1>It works!</h1>') except TimeoutError: return HttpResponseBadRequest("读取请求体超时") finally: # 取消超时定时器 signal.alarm(0)
方法2:手动读取WSGI输入流并校验
绕过Django默认的request.body读取逻辑,直接操作WSGI输入流读取指定长度内容,若无法获取足够数据则直接返回错误。
示例代码:
from django.http import HttpResponse, HttpResponseBadRequest def my_view(request): declared_length = request.META.get('CONTENT_LENGTH') content = b'' if declared_length is not None: try: declared_length = int(declared_length) # 从WSGI输入流读取指定长度内容 content = request.environ['wsgi.input'].read(declared_length) # 校验实际读取长度 if len(content) < declared_length: return HttpResponseBadRequest("请求体不完整") except (ValueError, IOError): return HttpResponseBadRequest("无效的请求体") # 后续业务逻辑处理 return HttpResponse('<h1>It works!</h1>')
注意事项
- 超时时间需根据业务场景合理设置,避免误拦截正常的慢请求。
- 若请求体启用了压缩(示例中客户端设置了
content-encoding: gzip),读取原始内容后需额外执行解压操作,才能进行后续业务处理。 - 不同WSGI服务器对输入流的处理逻辑可能存在差异,需结合实际部署环境测试验证。
内容的提问来源于stack exchange,提问作者Maxim Postnikov
相关产品推荐
相关产品推荐

