You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让DRF ViewSet的指定操作豁免自定义中间件?

DRF ViewSet指定操作豁免自定义中间件的解决办法

你碰到的问题是DRF ViewSet的路由特性导致的——请求到中间件process_view时,view_func是ViewSet类本身,不是触发请求的具体action方法,所以装饰器加的标记属性读不到。下面给两个实用的解决思路:

方法一:用装饰器标记action,中间件里找对应方法读标记

  1. 先写个简单的标记装饰器:
def exempt_middleware(func):
    # 给方法加个自定义属性当标记
    func._exempt_from_check = True
    return func
  1. 在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):
        # 你的业务逻辑
        ...
  1. 修改中间件的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名称,更直白:

  1. 在ViewSet里添加豁免列表:
class YourModelViewSet(viewsets.ModelViewSet):
    # 定义需要豁免中间件的action名称
    exempt_actions = ['batch_update', 'retrieve']

    @action(detail=True, methods=['post'])
    def batch_update(self, request, pk=None):
        ...
  1. 中间件里读取这个列表判断:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:24:53