如何让DRF ViewSet的指定操作豁免自定义中间件?
DRF ViewSet指定操作豁免自定义中间件的解决办法
你碰到的问题是DRF ViewSet的路由特性导致的——请求到中间件process_view时,view_func是ViewSet类本身,不是触发请求的具体action方法,所以装饰器加的标记属性读不到。下面给两个实用的解决思路:
方法一:用装饰器标记action,中间件里找对应方法读标记
- 先写个简单的标记装饰器:
def exempt_middleware(func): # 给方法加个自定义属性当标记 func._exempt_from_check = True return func
- 在ViewSet的目标action上加上这个装饰器:
from rest_framework import viewsets from rest_framework.decorators import action class YourModelViewSet(viewsets.ModelViewSet): # 普通操作,会被中间件检查 def list(self, request): return super().list(request) # 需要豁免的操作,加上装饰器 @action(detail=True, methods=['post']) @exempt_middleware def batch_update(self, request, pk=None): # 你的业务逻辑 ...
- 修改中间件的
process_view方法,找到当前请求对应的action方法,检查标记:
from django.utils.deprecation import MiddlewareMixin from rest_framework.viewsets import ViewSet class YourCheckMiddleware(MiddlewareMixin): def process_view(self, request, view_func, view_args, view_kwargs): # 先判断当前视图是不是DRF的ViewSet if hasattr(view_func, 'view_class') and issubclass(view_func.view_class, ViewSet): view_class = view_func.view_class # 获取当前要执行的action名称 action_name = view_kwargs.get('action') if not action_name: # 处理默认action(比如list、create这些没加@action的) method = request.method.lower() if method == 'get': action_name = 'list' if not view_kwargs.get('pk') else 'retrieve' elif method == 'post': action_name = 'create' elif method == 'put': action_name = 'update' elif method == 'patch': action_name = 'partial_update' elif method == 'delete': action_name = 'destroy' # 拿到对应的action方法 action_method = getattr(view_class, action_name, None) if action_method and getattr(action_method, '_exempt_from_check', False): # 有豁免标记,直接跳过中间件检查 return None # 没有豁免,执行你的中间件检查逻辑 # 比如:if not 检查通过: return HttpResponseForbidden() ...
方法二:在ViewSet里定义豁免列表,中间件直接匹配
如果不想用装饰器,直接在ViewSet里加个类属性指定豁免的action名称,更直白:
- 在ViewSet里添加豁免列表:
class YourModelViewSet(viewsets.ModelViewSet): # 定义需要豁免中间件的action名称 exempt_actions = ['batch_update', 'retrieve'] @action(detail=True, methods=['post']) def batch_update(self, request, pk=None): ...
- 中间件里读取这个列表判断:
class YourCheckMiddleware(MiddlewareMixin): def process_view(self, request, view_func, view_args, view_kwargs): if hasattr(view_func, 'view_class') and issubclass(view_func.view_class, ViewSet): view_class = view_func.view_class # 获取action名称,逻辑同方法一 action_name = view_kwargs.get('action') if not action_name: method = request.method.lower() if method == 'get': action_name = 'list' if not view_kwargs.get('pk') else 'retrieve' elif method == 'post': action_name = 'create' elif method == 'put': action_name = 'update' elif method == 'patch': action_name = 'partial_update' elif method == 'delete': action_name = 'destroy' # 检查是否在豁免列表里 if action_name in getattr(view_class, 'exempt_actions', []): return None # 执行检查逻辑 ...
这两种方法都能解决问题,方法一适合单个action灵活标记,方法二适合批量配置豁免操作。
内容的提问来源于stack exchange,提问作者browser-bug
相关产品推荐
相关产品推荐

