Django1.11.9自定义中间件更新POST请求失效问题求助
你遇到的问题核心在于Django原生请求对象和DRF请求对象的处理逻辑差异,以及ConvertTokenView内部的操作直接覆盖了你中间件添加的字段:
DRF的
request.data与Django原生request.POST的区别
DRF的Request是对Django原生request的封装,request.data是从原始请求体(比如JSON、form-data等)解析而来,并不完全依赖request.POST。如果你的请求是JSON格式,request.POST本身就是空的,中间件修改它自然不会被DRF的request.data捕获。ConvertTokenView的覆盖操作
在ConvertTokenView的post方法里,这段代码把request.data的内容全部覆盖到原生request.POST上:request._request.POST = request._request.POST.copy() for key, value in request.data.items(): request._request.POST[key] = value这意味着不管你中间件给原生
request.POST加了什么,都会被request.data里的原始内容覆盖——而request.data里本来就没有client_id和client_secret,所以最终这两个字段就消失了。中间件执行顺序的潜在影响
如果你的LoginMiddleware放在DRF的RequestMiddleware之后,DRF已经提前解析完request.data了,这时候你修改原生request.POST也不会同步到request.data里。
根据你的需求,这里有几个可行的方案,按推荐程度排序:
方案1:在中间件中直接修改DRF的request.data
判断当前请求是否是DRF的请求对象,如果是,直接给request.data添加字段,同时同步修改原生request.POST以兼容普通Django视图:
class LoginMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 处理DRF请求的data字段 if hasattr(request, 'data'): # 先复制为可修改对象(避免QueryDict不可变的问题) mutable_data = request.data.copy() mutable_data['client_id'] = '12345678' mutable_data['client_secret'] = '12345678' request.data = mutable_data # 处理原生Django请求的POST字段 request.POST = request.POST.copy() request.POST['client_id'] = '12345678' request.POST['client_secret'] = '12345678' response = self.get_response(request) return response
方案2:修改ConvertTokenView,在覆盖后添加字段
既然ConvertTokenView会覆盖request.POST,那可以在它覆盖完成后再把client_id和client_secret加进去,这样就不会被冲掉:
class ConvertTokenView(CsrfExemptMixin, OAuthLibMixin, APIView): # 保留原有属性配置 server_class = SocialTokenServer validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS oauthlib_backend_class = KeepRequestCore permission_classes = (permissions.AllowAny,) def post(self, request, *args, **kwargs): import pdb ; pdb.set_trace() # 使用rest framework的`.data`模拟django请求的POST主体 request._request.POST = request._request.POST.copy() for key, value in request.data.items(): request._request.POST[key] = value # 在这里添加字段,避免被之前的循环覆盖 request._request.POST['client_id'] = '12345678' request._request.POST['client_secret'] = '12345678' url, headers, body, status = self.create_token_response(request._request) response = Response(data=json.loads(body), status=status) for k, v in headers.items(): response[k] = v return response
这个方案不需要修改中间件,但缺点是需要自定义ConvertTokenView,后续依赖包更新时需要同步维护。
方案3:修改原始请求体(适合JSON格式请求)
如果你的请求是JSON格式,DRF是从request.body解析request.data的,所以可以在中间件中直接修改原始请求体内容:
import json class LoginMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 只处理POST请求且为JSON格式的情况 if request.method == 'POST' and request.content_type == 'application/json': try: body_data = json.loads(request.body) body_data['client_id'] = '12345678' body_data['client_secret'] = '12345678' # 重新构造request.body request.body = json.dumps(body_data).encode('utf-8') # 同步更新POST字段兼容非DRF视图 request.POST = request.POST.copy() request.POST['client_id'] = '12345678' request.POST['client_secret'] = '12345678' except json.JSONDecodeError: # 处理解析失败的情况(比如非JSON的POST请求) pass response = self.get_response(request) return response
这个方案最底层,能确保DRF解析request.data时就能拿到字段,但需要处理不同的请求格式(比如form-data),相对复杂。
- 确保你的
LoginMiddleware在DRF的中间件之前注册(在settings.py的MIDDLEWARE列表中,放在rest_framework.middleware.RequestMiddleware之前),这样中间件的修改才能被DRF的解析逻辑捕获。 - 如果使用方案1,要注意
request.data可能是不可变的QueryDict,必须先复制再修改。
内容的提问来源于stack exchange,提问作者Vadim Vzorov

