DRF的认证、权限、限流等八大组件

本文围绕Django REST framework展开,介绍了认证、权限、限流、过滤、排序、分页、异常处理及自动生成接口文档等组件的使用。包括不同组件的全局和局部配置方法,如认证方式有cookie、session、token等,还提及了自定义权限、限流类和分页器等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用组件需要用到登录,我们使用django内置admin站点并创建一个管理员.

admin运营站点的访问地址:http://127.0.0.1:8000/admin

python manage.py createsuperuser
# 如果之前有账号,但是忘了,可以通过终端下的命令修改指定用户的密码,这里的密码必须8位长度以上的
python manage.py changepassword 用户名

创建管理员以后,访问admin站点,先修改站点的语言配置

settings.py

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

认证Authentication

可以在配置文件中配置全局默认的认证方式/认证方案。

开发中常见的认证方式:cookie、sessiontoken

rest_framework/settings.py 默认配置文件

REST_FRAMEWORK = {
    # 配置认证方式的选项
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证[基于账号密码]
    )
}

可以在具体的视图类中通过设置类属性authentication_classess来设置单独的不同的认证方式

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication, BasicAuthentication

# Create your views here.
class AuthenticationAPIView(APIView):
    # 局部的认证方式,支持多个认证方法
    authentication_classes = [SessionAuthentication]
    def get(self,request):
        print(request.user)
        # AnonymousUser 就是在request.user无法识别当前访问的客户端时的游客用户对象
        return Response("ok")

认证失败会有两种可能的返回值,这个需要我们配合权限组件来使用:

  • 401 Unauthorized 未认证

  • 403 Permission Denied 权限被禁止

自定义认证:authentication.py

from rest_framework.authentication import BaseAuthentication
from django.contrib.auth import get_user_model  # 自动识别当前django系统中的系统用户模型


class CustomAuthentication(BaseAuthentication):
    """
    自定义认证方式
    """
    def authenticate(self, request):
        """核心认证方法"""
        user = request.query_params.get("user")
        pwd = request.query_params.get("pwd")
        if user != "root" or pwd != "houmen":
            return None

        # get_user_model获取当前系统中用户表对应的用户模型类
        user = get_user_model().objects.filter(is_superuser=1, is_active=1).first()
        return (user, None)  # 按照固定的返回格式填写 (用户模型对象, None)

视图调用自定义认证

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from .authentication import CustomAuthentication

# Create your views here.
class AuthenticationAPIView(APIView):
    # 局部的认证方式,支持多个认证方法
    authentication_classes = [CustomAuthentication]
    def get(self,request):
        print(request.user)
        # AnonymousUser 就是在request.user无法识别当前访问的客户端时的游客用户对象
        return Response("ok")

当然也可以注释掉上面视图中的配置,改成全局配置。settings.py,代码:

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    # 配置认证方式的选项【drf的认证是内部循环遍历每一个注册的认证类,一旦认证通过识别到用户身份,则不会继续循环】
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'drfdemo.authentication.CustomAuthentication',            # 自定义认证
        'rest_framework.authentication.SessionAuthentication',  # session认证
        'rest_framework.authentication.BasicAuthentication',      # 基本认证
    )
}

权限Permissions

权限控制可以限制用户对于视图的访问和对于具有模型对象的访问。

  • 在APIView视图的dispatch()方法中调用initial方法中先进行视图访问权限的判断

    self.check_permissions(request)

  • 在GenericAPIView通过get_object()获取具体模型对象时,会进行模型对象访问权限的判断

    self.check_object_permissions(self.request, obj)

使用

可以在配置文件restframework/settings.py中默认的全局设置了权限管理类,源码:

REST_FRAMEWORK = {
    ....
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',  # AllowAny 表示允许任何用户访问站点视图
    ],
}

如果要在项目覆盖默认配置rest_framework/settings.py的设置,则可以在项目配置文件中,settings.py,代码

"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
    # 配置认证方式的选项[全局配置]
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证[基于账号密码]
    ],
    # 配置权限的选项[全局配置]
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

也可以在具体的视图类中通过类属性permission_classes来进行局部设置,如

from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, AllowAny
from demo.serializers import StudentModelSerializer, Student
class PermissionAPIView(ModelViewSet):
    authentication_classes = [CustomAuthentication]
    permission_classes = [AllowAny]  # 针对封闭内部系统,登录页面时谁都可以访问。
    # permission_classes = [IsAuthenticated]
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializerfrom rest_framework.viewsets import ModelViewSet

提供的权限

  • AllowAny 允许所有用户进行操作访问,默认权限

  • IsAuthenticated 仅通过登录认证的用户进行操作访问

  • IsAdminUser 仅允许管理员用户进行操作访问

  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的游客只能查看数据。

from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser
from demo.serializers import StudentModelSerializer, Student
class PermissionAPIView(ModelViewSet):
    # authentication_classes = [CustomAuthentication]
    # permission_classes = [AllowAny]  # 针对封闭内部系统,登录页面时谁都可以访问。
    permission_classes = [IsAdminUser]  # 设置当前视图,只有是管理员可以访问操作。
    # permission_classes = [IsAuthenticatedOrReadOnly]  # 登录用户可以操作,而游客只能查看数据
    # permission_classes = [IsAuthenticated]
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

自定义权限

如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

  • has_permission(self, request, view)

    是否可以访问视图, view表示当前视图对象,request可以通过user属性获取当前用户

  • has_object_permission(self, request, view, obj)

    是否可以访问模型对象, view表示当前视图, obj为模型数据对象,request可以通过user属性获取当前用户

例如:在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:

from rest_framework.permissions import BasePermission


class VVIPPermission(BasePermission):
    """
    VVIP权限
    自定义权限,可用于全局配置,也可以用于局部配置
    """

    def has_permission(self, request, view):
        """
        视图权限
        返回结果未True则表示允许访问视图类
        request: 本次客户端提交的请求对象
        view: 本次客户端访问的视图类
        """
        # # 写在自己要实现认证的代码过程。
        identity = request.query_params.get("identity")
        # # 返回值为True,则表示通行
        return identity == "vvip"

    def has_object_permission(self, request, view, obj):
        """
        模型权限,写了视图权限(has_permission)方法,一般就不需要写这个了。
        返回结果未True则表示允许操作模型对象
        """
        from stuapi.models import Student
        if isinstance(obj, Student):
            # 限制只有小明才能操作Student模型
            identity = request.query_params.get("identity")
            return identity == "vvip"  # 如果身份不是vvip,返回值为False,不能操作
        else:
            # 操作其他模型,直接放行
            return True

view.py代码

from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser
from demo.serializers import StudentModelSerializer, Student
from .permissions import VVIPPermission


class PermissionAPIView(ModelViewSet):
    # authentication_classes = [CustomAuthentication]
    # permission_classes = [AllowAny]  # 针对封闭内部系统,登录页面时谁都可以访问。
    # permission_classes = [IsAdminUser]  # 设置当前视图,只有是管理员可以访问操作。
    # permission_classes = [IsAuthenticatedOrReadOnly]  # 登录用户可以操作,而游客只能查看数据
    # permission_classes = [IsAuthenticated]
    permission_classes = [VVIPPermission]
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

settings全局配置,代码:

"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
    # 配置认证方式的选项[全局配置]
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证[基于账号密码]
    ],
    # 配置权限的选项[全局配置]
    'DEFAULT_PERMISSION_CLASSES': [
        # 'rest_framework.permissions.IsAuthenticated',  # 如果将来的站点时封闭内部系统,则设置IsAuthenticated
        # 'rest_framework.permissions.AllowAny',         # 如果将来的站点时开放外部系统,则设置AllowAny
        'component.permissions.VVIPPermission'
    ]
}
注意

认证主要的作用就是识别客户端的访问者的身份,但是不能拦截客户端的访问。

权限是基于认证来实现的,但是权限可以针对不同身份的用户,进行拦截用户对视图、模型的访问操作。

限流 Throttling

可以对接口访问的频次进行限制,以减轻数据库的查询压力,或者实现特定的业务。

一般用于付费购买次数,投票等场景使用。

基本使用

可以在配置文件中,使用DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES进行全局配置,


"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
    # 配置认证方式的选项[全局配置]
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证[基于账号密码]
    ],
    # # 配置权限的选项[全局配置]
    # 'DEFAULT_PERMISSION_CLASSES': [
    #     # 'rest_framework.permissions.IsAuthenticated',  # 如果将来的站点时封闭内部系统,则设置IsAuthenticated
    #     # 'rest_framework.permissions.AllowAny',         # 如果将来的站点时开放外部系统,则设置AllowAny
    #     'component.permissions.VVIPPermission'
    # ],
    # 配置限流[全局配置]
    'DEFAULT_THROTTLE_CLASSES':[ # 限流配置类
        'rest_framework.throttling.AnonRateThrottle',  # 未登录认证的用户
        'rest_framework.throttling.UserRateThrottle',  # 已登录认证的用户
    ],
    'DEFAULT_THROTTLE_RATES': {  # 访问频率的全局配置
        'anon': '2/day',  # 针对游客的访问频率进行限制,实际上,drf只是识别首字母,但是为了提高代码的维护性,建议写完整单词
        'user': '5/day',  # 针对会员的访问频率进行限制,
    }
}

DEFAULT_THROTTLE_RATES限流配置可以支持使用 s(秒), m(分), h(时) 或d(天)来指明限流周期,对应的设置是在rest_framework/throttling.py的SimpleRateThrottle类中的parse_rate方法中设置的。源代码如下:

    def parse_rate(self, rate):
        """
        Given the request rate string, return a two tuple of:
        <allowed number of requests>, <period of time in seconds>
        """
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]   # 核心代码
        return (num_requests, duration)

限流配置也可以在具体视图类中通过类属性throttle_classess来局部配置,views.py,代码:

from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
class ThorttlingAPIView(APIView):
    throttle_classes = [UserRateThrottle, AnonRateThrottle]
    def get(self,request):
        return Response("ok")

可选限流类

限流类
AnonRateThrottle限制所有匿名未认证用户,使用IP区分用户。使用配置项DEFAULT_THROTTLE_RATES['anon'] 来设置频次
UserRateThrottle限制认证用户,使用User模型的 id主键 来区分。使用配置项DEFAULT_THROTTLE_RATES['user'] 来设置频次
ScopedRateThrottle限制用户对于每个视图类的访问频次,使用ip或user id。使用视图类中的throttle_scope设置限流频次的变量名,假设是a,则可以使用配置项DEFAULT_THROTTLE_RATES['a']来设置频次

"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
    # 配置认证方式的选项[全局配置]
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication', # session认证
        'rest_framework.authentication.BasicAuthentication',   # basic认证[基于账号密码]
    ],
    # # 配置权限的选项[全局配置]
    # 'DEFAULT_PERMISSION_CLASSES': [
    #     # 'rest_framework.permissions.IsAuthenticated',  # 如果将来的站点时封闭内部系统,则设置IsAuthenticated
    #     # 'rest_framework.permissions.AllowAny',         # 如果将来的站点时开放外部系统,则设置AllowAny
    #     'component.permissions.VVIPPermission'
    # ],
    # 配置限流[全局配置]
    'DEFAULT_THROTTLE_CLASSES':[ # 限流配置类
        # 'rest_framework.throttling.AnonRateThrottle',    # 未登录认证的用户
        # 'rest_framework.throttling.UserRateThrottle',    # 已登录认证的用户
        'rest_framework.throttling.ScopedRateThrottle',  # 已视图作为识别单位进行限流
    ],
    'DEFAULT_THROTTLE_RATES': {  # 频率配置
        'anon': '3/h',   # 针对游客的访问频率进行限制,实际上,drf只是识别首字母,但是为了提高代码的维护性,建议写完整单词
        'user': '10/h',  # 针对会员的访问频率进行限制,
        'thorttle': '5/h',  # 已视图中的throttle_scope进行设置限流频率
    }
}

视图代码

from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
class ThorttlingAPIView(APIView):
    # throttle_classes = [UserRateThrottle, AnonRateThrottle]
    throttle_scope = "thorttle"

    def get(self,request):
        return Response("ok")

class Thorttling2APIView(APIView):
    # throttle_classes = [UserRateThrottle, AnonRateThrottle]
    throttle_scope = "thorttle"

    def get(self,request):
        return Response("ok")

过滤Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展模块来增强支持。

conda install django-filter

settings.py,代码:

INSTALLED_APPS = [
    # ....
    'django_filters',
]

"""drf配置信息必须全部写在REST_FRAMEWORK配置项中"""
REST_FRAMEWORK = {
    # ....代码省略。。。
    
    # 过滤查询,全局配置
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend'
    ],
}

在具体的视图类中添加类属性filterset_fields,指定可以过滤的字段

from rest_framework.generics import ListAPIView

class FilterAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 设置当前列表视图的过滤字段
    filterset_fields = ["sex", "classmate", "age"]
    
# 单个字段过滤
# http://127.0.0.1:8000/component/list/?classmate=303
# http://127.0.0.1:8000/component/list/?sex=1
# 多个字段过滤
# http://127.0.0.1:8000/component/list/?age=20&classmate=303

局部设置就是直接在视图类中设置类属性filter_backends调用的过滤器类

from rest_framework.generics import ListAPIView
from django_filters.rest_framework import DjangoFilterBackend

class FilterAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 设置过滤器类,局部设置
    filter_backends = [DjangoFilterBackend]
    # 设置当前列表视图的过滤字段
    filterset_fields = ["sex", "classmate", "age"]

排序Ordering

对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。

在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。

前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。

全局配置,settings.py,代码:


"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
   # 中间代码省略。。。。。
   # 中间代码省略。。。。。

    # # 查询过滤[全局配置]
    'DEFAULT_FILTER_BACKENDS': [
        # 'django_filters.rest_framework.DjangoFilterBackend',  # 过滤
        'rest_framework.filters.OrderingFilter',  # 排序
    ],
}

视图代码

class OrderAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 设置当前列表视图的排序字段
    ordering_fields = ['id', 'age']

# http://127.0.0.1:8000/component/order/?ordering=-id
# -id 表示针对id字段进行倒序排序
# id  表示针对id字段进行升序排序

局部设置,在视图类中使用filter_backends设置当前视图类中使用的排序类,views.py,代码:

from rest_framework.filters import OrderingFilter
class OrderAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 局部设置当前列表视图的排序类[如果使用局部配置项filter_backends会自动覆盖全局配置的DEFAULT_FILTER_BACKENDS]
    filter_backends = [OrderingFilter]
    # 设置当前列表视图的排序字段
    ordering_fields = ['id', 'age']

上面提到,因为过滤和排序公用了一个配置项filter_backends,所以如果排序和过滤要一起使用的话则必须整个项目,要么一起全局过滤排序,要么一起局部过滤排序。绝不能出现,一个全局,一个局部的这种情况,局部配置项filter_backends会自动覆盖全局配置的DEFAULT_FILTER_BACKENDS。ff

from rest_framework.viewsets import ModelViewSet
from students.models import Student
from students.serializers import StudentModelSerializer
# Create your views here.
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
class StuListAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 局部设置过滤器类与排序类
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    # 设置当前列表视图的过滤字段
    filterset_fields = ["id", "classmate", "sex"]
    # 设置当前列表视图的排序字段
    ordering_fields = ['id', 'age']

# http://127.0.0.1:8000/component/stu/?ordering=-id&classmate=301

分页Pagination

因为django默认提供的分页器主要使用于前后端不分离的业务场景,所以REST framework也提供了针对接口数据的分页支持。

我们可以在配置文件settings.py中进行全局分页配置,代码:


"""drf的配置"""
# from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
REST_FRAMEWORK = {
    # 中间代码省略......
    # 中间代码省略......
    # 列表分页[全局配置,对整站所有的列表页视图都会进行分页处理]
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 以page参数作为分页参数
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',              # 以limit和offset作为分页参数
    'PAGE_SIZE': 2,  # 每页数目,如果不设置,则没有进行分配
}

上面直接访问任意一个列表页视图的api接口,可以看到如下效果:

{
    "count": 16,   // 本次分页的总数据量
    "next": "http://127.0.0.1:8000/component/page/?page=3",  // 下一页数据所在的地址
    "previous": "http://127.0.0.1:8000/component/page/",         // 上一页数据所在的地址
    "results": [    // 当前页数据的列表项
        {
            "id": 6,
            "name": "xiaoming",
            "sex": true,
            "age": 20,
            "classmate": "303",
            "description": "hello world"
        },
       // .....
    ]
}

如果在settings.py配置文件中设置了全局分页,那么在drf中凡是调用了ListModelMixin的list()都会自动分页。如果项目中出现大量需要分页的数据,只有少数部分的不需要分页,则可以在少部分的视图类中关闭分页功能。可以在视图类中设置如下:

class PageAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = None  # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页

可选分页器

PageNumberPagination

前端访问网址形式:

GET  http://127.0.0.1:8000/component/page/?page=2

可以在子类中定义的属性:

参数名描述
page_size每页数据量的数量,默认是没有设置的,我们可以在配置分页时通过PAGE_SIZE设置设置
page_query_paramurl地址栏上当前页码的参数名,默认为"page"
page_size_query_paramurl地址栏上代表每一页数据量的参数名,默认是None,也就是不允许地址栏修改每一页数据量。
max_page_size限制url地址栏上设置每一页数据量的最大值,前提是已经设置了page_size_query_param

例如在当前子应用下的`paginations.py`创建一个自定义分页器类,代码:

from rest_framework.pagination import PageNumberPagination
# PageNumberPagination,以页码作为分页条件
# page=1&page_size=10      第1页
# page=2&page_size=10      第2页
# ...
class StudentPageNumberPagination(PageNumberPagination):
    page_size = 5  # 每一页默认实现的数据量
    page_query_param = 'page'  # 页码
    page_size_query_param = 'page_size'  # 每一页数据量的设置变量
    max_page_size = 10  # 限制 page_size_query_param 的最大值

视图中使用自定义分页类,视图代码:


from .paginations import StudentPageNumberPagination

class PageAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # pagination_class = None  # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页
    pagination_class = StudentPageNumberPagination
from  rest_framework.pagination import PageNumberPagination,LimitOffsetPagination

# LimitOffsetPagination,以数据库查询的limit和offset数值作为分页条件
# limit=10&offset=0   第1页
# limit=10&offset=10  第2页
# ...

LimitOffsetPagination

前端访问网址形式:

GET http://127.0.0.1:8000/component/page/?limit=5&offset=10

可以在子类中定义的属性:

参数描述
default_limit默认每一页展示数据的数量,默认值与PAGE_SIZE一样的
limit_query_param默认'limit',default_limit的地址栏参数名
offset_query_param查询数据的开始偏移量,相当于上面的page参数,默认'offset'
max_limit限制地址栏对每一页数据展示的最大数量。默认None

子定义分页类,paginations.py,代码:

from  rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
# 中间省略。。。
# LimitOffsetPagination,以数据库查询的limit和offset数值作为分页条件
# limit=10&offset=0   第1页
# limit=10&offset=10  第2页

# 中间省略。。。

class Student1LimitOffsetPagination(LimitOffsetPagination):
    default_limit = 5 # 每一页的数据量
    offset_query_param = "offset" # 查询字符串中代表页码的变量名
    limit_query_param = "limit" # 查询字符串中代表每一页数据的变量名
    max_limit = 10   # 允许客户端通过查询字符串调整的最大单页数据量

视图代码

from .paginations import StudentPageNumberPagination, Student1LimitOffsetPagination

class PageAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # pagination_class = None  # 关闭来自全局配置的分页设置,设置当前列表视图不需要分页
    # pagination_class = StudentPageNumberPagination
    pagination_class = Student1LimitOffsetPagination
    
  # 访问地址:http://127.0.0.1:8000/component/page/

异常处理 Exceptions

REST framework本身在APIView提供了异常处理,但是仅针对drf内部现有的接口开发相关的异常进行格式处理,但是开发中我们还会使用到各种的数据或者进行各种网络请求,这些都有可能导致出现异常,这些异常在drf中是没有进行处理的,所以就会冒泡给django框架了,django框架会进行组织错误信息,作为html页面返回给客户端,所在在前后端分离项目中,可能js的ajax无法理解或者无法接收到这种数据,甚至导致js出现错误的情况。因此为了避免出现这种情况,我们可以自定义一个属于自己的异常处理函数,对于drf无法处理的异常,我们自己编写异常处理的代码逻辑。

针对于现有的drf的异常处理进行额外添加属于开发者自己的逻辑代码,一般我们编写的异常处理函数,会写一个公共的目录下或者主应用目录下。这里主应用下直接创建一个excepitions.py,代码:

from django.core.exceptions import ObjectDoesNotExist
from rest_framework.views import exception_handler, Response, status



def custom_exception_handler(exc, context):
    """
    自定义异常函数
    exc: 异常实例对象,发生异常时实例化出来的
    context: 字典,异常发生时python解释器会自动收集异常的执行上下文信息。
             所谓的执行上下文context就是python解释器在执行代码时保存在内存中的变量、函数、类、对象、模块、以及异常出现的路径,代码的行号等,等一系列的信息组成的环境信息。
    """
    # 1. 先让drf处理它能处理的异常
    response = exception_handler(exc, context)
    if response is None:
        """response的值为None,则表示当前异常无法drf处理"""
        if isinstance(exc, ZeroDivisionError):
            response = Response({"detail": "数学老师还有30秒达到战场,0不能作为除数!"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        if isinstance(exc, ObjectDoesNotExist):
            response = Response({"detail": "当前模型对象不存在!"}, status=status.HTTP_404_NOT_FOUND)

    return response

在配置文件中声明自定义的异常处理,settings.py,代码:

REST_FRAMEWORK = {
    # 异常配置
    'EXCEPTION_HANDLER': 'drfdemo.exceptions.custom_exception_handler',
}

如果未声明自定义异常的话,drf会采用默认的方式,使用自己封装的异常处理函数,rest_framework/settings.py

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',  # from rest_framework.views import exception_handler
}

REST framework内置的异常类

异常类描述
APIExceptiondrf的所有异常的父类
ParseError解析错误
AuthenticationFailed认证失败
NotAuthenticated尚未认证
PermissionDenied权限拒绝
NotFound404 未找到
MethodNotAllowed请求方式不支持
NotAcceptable要获取的数据格式不支持
UnsupportedMediaType不支持的媒体格式
Throttled超过限流次数
ValidationError校验失败

自动生成接口文档

REST framework可以自动帮助我们生成接口文档。drf的接口文档多数以网页的方式呈现,自动接口文档能生成的是继承自`APIView`及其子类的视图。

coreapi

安装依赖

REST framewrok生成接口文档需要coreapi库的支持。

conda install coreapi

设置接口文档访问路径

在settings.py中配置接口文档的模块到项目中。

INSTALLED_APPS = [
 
    'coreapi',


]
REST_FRAMEWORK = {
    # 。。。 其他选项
    # 配置自动生成接口文档的模式
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
}

在总路由中添加接口文档路径。

文档路由对应的视图配置为rest_framework.documentation.include_docs_urls

参数title为接口文档网站的标题。总路由urls.py,代码:

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站点页面标题'))
]

yasg

Swagger是一个规范和完整的自动化接口开发框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统源代码作为服务器以同样的速度来更新。当接口有变动时,对应的接口文档也会自动更新。如:接口测试站点(httpbin.org),也是利用Swagger来生成接口文档。

Swagger在线编辑器:Swagger Editor

官方文档:drf-yasg — drf-yasg 1.21.7 documentation

Github:GitHub - axnsan12/drf-yasg: Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.

<think>好的,我现在需要帮助用户了解在前后端分离架构中使用Django Rest Framework(DRF)实现限流功能的方案。首先,我得回顾一下DRF限流机制,确保自己理解正确。限流主要是为了防止API被滥用,确保服务的稳定性,这在前后端分离的项目中尤为重要,因为前端可能频繁调用API。 首先,用户提到的是前后端分离的情况,所以需要确保限流设置不会影响到前端正常的请求,同时能有效阻止恶意或过量的请求。根据引用[2],DRF提供了内置的限流组件,包括AnonRateThrottle和UserRateThrottle,分别用于匿名用户和认证用户。这可能是一个起点。 接下来,我需要考虑如何配置这些限流类。在settings.py中,应该添加DEFAULT_THROTTLE_CLASSES和DEFAULT_THROTTLE_RATES。例如,设置匿名用户每分钟10次,认证用户每分钟100次。同时,要确保已经配置了认证权限,因为限流通常在认证之后处理。引用[3]提到了使用JWT进行认证,可能需要结合djangorestframework-simplejwt包,但限流本身不直接依赖JWT,而是依赖DRF认证机制。 然后,可能需要自定义限流策略。比如,针对不同的视图或API端点设置不同的频率限制。例如,关键API限制严格一些,其他则宽松。这里需要覆盖视图的throttle_classes属性,或者使用ScopedRateThrottle,根据作用域来设置。 另外,用户可能需要了解如何测试限流是否生效。可以通过频繁发送请求并检查返回的429状态码来验证。此外,处理限流时的响应信息也很重要,需要友好地提示用户,比如返回JSON数据包含错误信息和重试时间。 还需要注意DRF限流是基于缓存机制的,所以必须配置缓存后端,比如使用Memcached或Redis,而不是默认的本地内存缓存,特别是在生产环境中。引用[2]中提到限流技术防止API滥用,所以正确的缓存配置是关键。 可能用户会遇到的常见问题包括限流配置未生效,这时候需要检查中间件的顺序,确保认证权限类已正确配置,以及缓存是否正常工作。另外,如果使用多个限流类,DRF会取所有限流中最严格的那个,这点需要注意。 最后,应该提到监控和日志记录,帮助追踪限流事件,分析流量模式,进一步优化限流策略。例如,使用Django的日志模块记录被限流的请求,或者集成监控工具如Prometheus。 总结一下,实现步骤应该是:安装必要包,配置认证权限,设置限流类和频率,可选自定义策略,测试验证,处理响应,配置缓存,以及监控。确保每一步都清晰,并参考DRF文档和引用中的信息来确保正确性。</think>在前后端分离架构中,使用Django Rest Framework (DRF) 实现API限流功能可通过以下步骤完成: ### 一、DRF限流核心配置 1. **安装依赖** 确保已安装`djangorestframework`和缓存依赖(如Redis): ```bash pip install djangorestframework redis ``` 2. **基础配置** 在`settings.py`中添加限流配置: ```python REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', # 匿名用户限流 'rest_framework.throttling.UserRateThrottle' # 认证用户限流 ], 'DEFAULT_THROTTLE_RATES': { 'anon': '10/minute', # 匿名用户每分钟10次 'user': '100/minute' # 认证用户每分钟100次 } } ``` 需确保`rest_framework`已加入`INSTALLED_APPS`[^2]。 ### 二、结合认证机制 1. **配置JWT认证** 若使用JWT(推荐方案),需安装`djangorestframework-simplejwt`并配置: ```python REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ) } ``` 通过JWT令牌中的用户信息实现精准限流[^3]。 ### 三、自定义限流策略 1. **视图级限流** 在特定视图中覆盖默认配置: ```python from rest_framework.throttling import ScopedRateThrottle class SensitiveAPIView(APIView): throttle_classes = [ScopedRateThrottle] throttle_scope = 'sensitive' # 自定义作用域 ``` 在`settings.py`中补充速率: ```python 'DEFAULT_THROTTLE_RATES': { 'sensitive': '3/minute' # 敏感接口每分钟3次 } ``` 2. **动态频率控制** 通过继承`SimpleRateThrottle`实现基于请求参数的动态限流: ```python class IPThrottle(throttling.SimpleRateThrottle): scope = 'ip' def get_cache_key(self, request, view): return self.get_ident(request) # 基于客户端IP限流 ``` ### 四、生产环境优化 1. **缓存配置** 使用Redis作为限流计数器存储(需安装`django-redis`): ```python CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", } } ``` 2. **限流响应优化** 自定义超过频率限制时的响应格式: ```python class CustomThrottle(throttling.UserRateThrottle): def wait(self): return self.num_requests / self.duration # 返回剩余等待时间 def throttle_failure(self): data = { "code": 429, "message": f"请求过快,请{self.wait():.1f}秒后重试" } raise throttling.Throttled(detail=data) ``` ### 五、验证与测试 1. **使用DRF测试客户端** ```python from rest_framework.test import APIClient client = APIClient() for _ in range(11): response = client.get('/api/sensitive-data/') print(response.status_code) # 第11次应返回429 ``` 2. **监控限流日志** 在`settings.py`中添加日志记录: ```python LOGGING = { 'loggers': { 'django.throttle': { 'handlers': ['file'], 'level': 'INFO', 'propagate': True, } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Echo.....

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值