python面试题:web框架(三)

三、DRF(Django REST Framework)基础

  1. DRF 的核心组件有哪些?(Serializers、ViewSets、Routers、Authentication、Permissions)

    1. Django REST framework(简称 DRF)是用于构建 Web API 的强大且灵活的工具包。它的核心组件主要包括以下几个:

      1. Serializers(序列化器):
        • 功能:将复杂的数据类型(如 Django 模型实例)转换为 Python 原生数据类型,以便渲染为 JSON、XML 等格式;也负责反序列化,将数据验证并保存回数据库。
        • 用途:定义数据的输入和输出格式,类似于 Django 的 Form 或 ModelForm。
        • 示例类:Serializer、ModelSerializer。
      2. ViewSets(视图集):
        • 功能:将一组相关的视图逻辑组织在一起,简化代码,提高可重用性。
        • 特点:通过组合不同的动作(如 list、create、retrieve、update、destroy)来处理标准的 CRUD 操作。
        • 与 APIView 相比,ViewSet 更简洁抽象,常与 Router 配合使用。
        • 示例类:ModelViewSet、ReadOnlyModelViewSet、GenericViewSet。
      3. Routers(路由器):
        • 功能:自动为 ViewSet 生成 URL 配置,减少手工编写 URLconf 的工作。
        • 优势:根据 ViewSet 中定义的动作自动映射 URL 模式。
        • 常见路由器:DefaultRouter、SimpleRouter。
      4. Authentication(认证):
        • 功能:识别用户身份,确定请求是由哪个用户发起的。
        • 内置方式包括:TokenAuthentication、SessionAuthentication、BasicAuthentication,以及第三方如 JWT。
        • 使用位置:通常在 settings.py 中全局设置或在视图/视图集中局部设置。
      5. Permissions(权限):
        • 功能:控制用户对 API 的访问权限,决定某个用户是否有权限执行某项操作。
        • 常见权限类:
          • AllowAny:允许任何人访问。
          • IsAuthenticated:仅认证用户可以访问。
          • IsAdminUser:仅管理员用户可以访问。
          • IsAuthenticatedOrReadOnly:未认证用户只读。
          • 自定义权限类:继承 BasePermission。
        • 可在全局、视图或视图集级别设置。

      除了以上核心组件外,DRF 还有其他重要部分如:

      • Request & Response 对象:增强对 HTTP 请求和响应的处理能力。
      • Generic Views(通用视图):提供可复用的基于类的视图,如 ListCreateAPIView、RetrieveUpdateDestroyAPIView。
      • Pagination(分页):控制返回数据的数量和格式。
      • iltering(过滤)、Ordering(排序)、Throttling(限流)等高级功能。

      总结:

      DRF 的核心组件主要包括:

      ✅ Serializers

      ✅ ViewSets

      ✅ Routers

      ✅ Authentication

      ✅ Permissions

      这些组件共同构成了 DRF 强大而灵活的 API 开发体验。

  2. Serializer 的作用是什么?ModelSerializer 与 Serializer 的区别?

    1. 在 Django REST framework(DRF)中,Serializer(序列化器)起着至关重要的作用。下面分别说明 Serializer 的作用,以及 ModelSerializer 与 Serializer 的区别。


      一、Serializer 的作用是什么?

      Serializer 是 DRF 中的核心组件之一,主要作用如下:

      1. 序列化(Serialization)

      将复杂的数据类型(如 Django 模型实例、QuerySets 等)转换为 Python 原生数据类型(如 dict、list),进而可以轻松地渲染成 JSON、XML 等格式供前端或其他系统使用。

      # 示例:将模型实例转为字典
      serializer = UserSerializer(user_instance)
      data = serializer.data  # {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}
      

      2. 反序列化(Deserialization)

      接收前端传来的原始数据(如 JSON),将其转换并验证后还原为 Python 数据类型,并可进一步保存到数据库中。

      # 示例:将 JSON 转为模型实例
      serializer = UserSerializer(data=request.data)
      if serializer.is_valid():
          user = serializer.save()  # 创建新的模型实例
      

      3. 数据验证(Validation)

      在反序列化过程中,可以对数据进行校验(字段验证、跨字段验证等),确保数据符合业务规则。

      def validate_email(self, value):
          if not value.endswith('@example.com'):
              raise serializers.ValidationError("Email must be @example.com domain")
          return value
      

      4. 简化视图逻辑

      通过封装序列化/反序列化和验证逻辑,使视图更简洁、清晰。


      二、ModelSerializer 与 Serializer 的区别

      对比项 Serializer ModelSerializer
      定义方式 手动定义所有字段 自动根据模型生成字段
      字段来源 需显式声明每个字段 使用 model 属性自动推断字段
      Meta 类支持 无特殊要求 使用 class Meta 指定模型和字段
      创建/更新方法 默认不实现 create() 和 update() 自动生成 create() 和 update() 方法
      适用场景 非模型数据、复杂嵌套结构 与 Django 模型直接对应的 API
      灵活性 高,完全控制字段和行为 快速开发,适用于标准 CRUD

      示例对比

      使用 Serializer(手动定义字段):

      class UserSerializer(serializers.Serializer):
          id = serializers.IntegerField(read_only=True)
          name = serializers.CharField(max_length=100)
          email = serializers.EmailField()
      
          def create(self, validated_data):
              return User.objects.create(**validated_data)
      
          def update(self, instance, validated_data):
              instance.name = validated_data.get('name', instance.name)
              instance.email = validated_data.get('email', instance.email)
              instance.save()
              return instance
      

      使用 ModelSerializer(自动生成字段):

      class UserSerializer(serializers.ModelSerializer):
          class Meta:
              model = User
              fields = ['id', 'name', 'email']
              # 或使用 fields = '__all__' 包含所有字段
      

      ✅ ModelSerializer 本质上继承自 Serializer,但提供了大量自动化功能,适合快速开发基于模型的 API。


      三、总结

      问题 回答
      Serializer 的作用? 实现数据的序列化与反序列化、验证数据、简化视图逻辑。
      ModelSerializer vs Serializer 区别? ModelSerializer 自动根据模型生成字段和方法,适合模型驱动的开发;Serializer 更灵活,适合非模型数据或需要精细控制的场景。

      在实际开发中,如果 API 是对 Django 模型的 CRUD 操作,优先使用 ModelSerializer;若处理非模型数据或需要高度定制,则使用 Serializer。

  3. 如何在 DRF 中实现字段验证(validate_xxx 方法)?

    1. 在 Django REST Framework(DRF)中,字段验证是确保传入数据符合业务规则的重要环节。DRF 提供了多种方式进行字段验证,其中 validate_<field_name> 方法 是一种常用且强大的机制,用于对单个字段进行自定义验证。


      一、validate_<field_name>方法的作用

      • 用于对单个字段进行自定义验证。
      • 方法命名规则:validate_<字段名>(例如 validate_emailvalidate_password)。
      • 当调用 serializer.is_valid()时,DRF 会自动调用这些方法。
      • 验证失败应抛出 serializers.ValidationError异常。
      • 验证成功应返回该字段的“清理后”的值。

      二、基本用法示例

      假设我们有一个 UserSerializer,需要对 email字段进行域名限制验证:

      from rest_framework import serializers
      from django.core.exceptions import ValidationError
      
      class UserSerializer(serializers.Serializer):
          username = serializers.CharField(max_length=100)
          email = serializers.EmailField()
      
          def validate_email(self, value):
              # 检查邮箱是否以 @example.com 结尾
              if not value.endswith('@example.com'):
                  raise serializers.ValidationError("邮箱必须使用 @example.com 域名")
              return value  # 必须返回处理后的值
      

      使用方式:

      data = {'username': 'alice', 'email': 'alice@gmail.com'}
      serializer = UserSerializer(data=data)
      
      if serializer.is_valid():
          print("验证通过:", serializer.validated_data)
      else:
          print("验证失败:", serializer.errors)
          # 输出: {'email': [ErrorDetail(string='邮箱必须使用 @example.com 域名', code='invalid')]}
      

      ✅ 注意:必须返回 value,否则该字段在 validated_data中为 None


      三、结合 ModelSerializer 使用

      validate_<field_name>同样适用于 ModelSerializer

      class UserSerializer(serializers.ModelSerializer):
          class Meta:
              model = User
              fields = ['username', 'email']
      
          def validate_username(self, value):
              if len(value) < 3:
                  raise serializers.ValidationError("用户名至少需要3个字符")
              return value
      

      四、其他验证方法补充

      除了 validate_<field_name>,DRF 还支持以下验证方式:

      1. 整体验证:validate()方法

      用于需要多个字段联合验证的场景(如密码确认)。

      def validate(self, data):
          if data['password'] != data['confirm_password']:
              raise serializers.ValidationError("两次输入的密码不一致")
          return data
      

      2. 字段级约束(直接在字段上设置)

      email = serializers.EmailField(max_length=100, required=True)
      age = serializers.IntegerField(min_value=0, max_value=150)
      

      3. 自定义验证器(Validator 列表)

      from rest_framework.validators import UniqueValidator
      
      username = serializers.CharField(
          max_length=100,
          validators=[UniqueValidator(queryset=User.objects.all(), message="用户名已存在")]
      )
      

      五、完整示例

      class RegisterSerializer(serializers.ModelSerializer):
          password = serializers.CharField(write_only=True)
          confirm_password = serializers.CharField(write_only=True)
      
          class Meta:
              model = User
              fields = ['username', 'email', 'password', 'confirm_password']
      
          def validate_email(self, value):
              if not value.endswith('@mysite.com'):
                  raise serializers.ValidationError("仅支持 @mysite.com 邮箱注册")
              return value
      
          def validate_username(self, value):
              if User.objects.filter(username=value).exists():
                  raise serializers.ValidationError("用户名已被占用")
              return value
      
          def validate(self, data):
              if data['password'] != data['confirm_password']:
                  raise serializers.ValidationError({"confirm_password": "两次密码输入不一致"})
              return data
      
          def create(self, validated_data):
              validated_data.pop('confirm_password')
              user = User.objects.create_user(**validated_data)
              return user
      

      六、总结

      验证方式 说明 使用场景
      validate_<field> 单字段自定义验证 邮箱格式、用户名长度等
      validate() 多字段联合验证 密码确认、开始时间 < 结束时间
      字段参数(min_length, max_value等) 简单约束 基础数据范围控制
      validators列表 复用验证逻辑 唯一性检查、正则匹配等

      关键点

      • validate_<field_name>是 DRF 中实现字段级验证的核心方法。
      • 验证失败时抛出 serializers.ValidationError
      • 验证成功后务必返回字段值。
      • 可与其他验证方式结合使用,构建健壮的数据校验体系。
  4. ViewSet 和 APIView 的区别?什么时候用哪个?

    1. 在 Django REST framework(DRF)中,APIViewViewSet都是用于构建 API 视图的核心类,但它们在设计理念、使用方式和适用场景上有明显区别。下面详细对比二者的差异,并说明何时选择哪一个。


      一、APIView 与 ViewSet 的核心区别

      对比维度 APIView ViewSet
      基类来源 继承自 View(Django 原生视图) 继承自 APIView,进一步封装
      设计思想 面向“HTTP 方法”编程(如 get、post) 面向“资源操作”编程(如 list、create、retrieve)
      URL 绑定方式 手动为每个视图函数绑定 URL(如 path('users/', UserList.as_view()) 通常与 Router 配合,自动生成 URL
      方法定义 重写 .get().post()等方法 重写 .list().create().retrieve()等动作方法
      代码复用性 较低,每个视图独立定义 高,通过继承 ModelViewSet等可复用 CRUD 逻辑
      适用场景 定制化强、非标准 RESTful 接口 标准 CRUD 操作、RESTful API 快速开发
      灵活性 高,完全控制请求处理逻辑 稍低,但可通过 @action装饰器扩展

      二、代码示例对比

      1. 使用 APIView 实现用户列表和详情

      # views.py
      from rest_framework.views import APIView
      from rest_framework.response import Response
      from .models import User
      from .serializers import UserSerializer
      
      class UserList(APIView):
          def get(self, request):
              users = User.objects.all()
              serializer = UserSerializer(users, many=True)
              return Response(serializer.data)
      
          def post(self, request):
              serializer = UserSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=201)
              return Response(serializer.errors, status=400)
      
      class UserDetail(APIView):
          def get_object(self, pk):
              from django.shortcuts import get_object_or_404
              return get_object_or_404(User, pk=pk)
      
          def get(self, request, pk):
              user = self.get_object(pk)
              serializer = UserSerializer(user)
              return Response(serializer.data)
      
          def put(self, request, pk):
              user = self.get_object(pk)
              serializer = UserSerializer(user, data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=400)
      
          def delete(self, request, pk):
              user = self.get_object(pk)
              user.delete()
              return Response(status=204)
      # urls.py
      urlpatterns = [
          path('users/', UserList.as_view()),
          path('users/<int:pk>/', UserDetail.as_view()),
      ]
      

      ✅ 优点:完全控制每个方法的逻辑。

      ❌ 缺点:重复代码多,URL 配置繁琐。


      2. 使用 ViewSet 实现相同功能

      # views.py
      from rest_framework import viewsets
      from .models import User
      from .serializers import UserSerializer
      
      class UserViewSet(viewsets.ModelViewSet):
          queryset = User.objects.all()
          serializer_class = UserSerializer
      # urls.py
      from rest_framework.routers import DefaultRouter
      from .views import UserViewSet
      
      router = DefaultRouter()
      router.register(r'users', UserViewSet)
      
      urlpatterns = router.urls
      

      ✅ 自动生成以下 URL:

      • GET /users/→ list
      • POST /users/→ create
      • GET /users/{id}/→ retrieve
      • PUT /users/{id}/→ update
      • DELETE /users/{id}/→ destroy

      三、什么时候用 APIView?什么时候用 ViewSet?

      ✅ 使用 APIView 的场景:

      1. 非 RESTful 接口:比如需要处理复杂的业务逻辑或多个数据源混合。
      2. 高度定制化需求:每个 HTTP 方法的处理流程完全不同,无法复用。
      3. 非 CRUD 操作:如文件上传、第三方 API 转发、Webhook 处理等。
      4. 需要精细控制请求/响应流程:如自定义认证、限流、缓存策略等。
      5. 一个视图只对应一个 URL 和一个方法(如仅 POST 的登录接口)。

      📌 示例:实现一个支付回调接口,只处理 POST 请求,逻辑复杂且与模型无关。

      class PaymentCallback(APIView):
          def post(self, request):
              # 复杂的签名验证、异步任务触发等
              ...
      

      ✅ 使用 ViewSet 的场景:

      1. 标准的 CRUD 操作:对单个模型进行增删改查。
      2. 快速开发 RESTful API:希望减少样板代码。
      3. 希望自动生成 URL:配合 Router 实现“零配置”路由。
      4. 需要复用通用逻辑:如使用 ModelViewSet快速搭建完整资源接口。
      5. 需要扩展自定义动作:通过 @action添加额外端点(如 /users/{id}/set-admin/)。

      📌 示例:管理后台的用户管理模块,支持列表、详情、创建、禁用等操作。

      class UserViewSet(viewsets.ModelViewSet):
          queryset = User.objects.all()
          serializer_class = UserSerializer
      
          @action(detail=True, methods=['post'])
          def set_admin(self, request, pk=None):
              user = self.get_object()
              user.is_staff = True
              user.save()
              return Response({'status': 'admin set'})
      

      四、总结:如何选择?

      需求 推荐方案
      快速开发标准 CRUD API ViewSet(特别是 ModelViewSet
      完全控制请求处理流程 APIView
      需要与 Router 集成自动生成 URL ViewSet
      非 RESTful、复杂业务逻辑 APIView
      想减少重复代码 ViewSet
      需要自定义非标准动作 ViewSet + @action

      五、进阶建议

      • 从 APIView 开始学习:理解 HTTP 方法和请求生命周期。
      • 优先使用 ViewSet 提升效率:尤其是在构建资源型 API 时。
      • 混用两者:DRF 允许在同一个项目中同时使用 APIViewViewSet,根据实际需求选择最合适的工具。

      💡 记住:ViewSet 不是替代 APIView,而是更高层次的抽象。理解底层原理后,才能灵活运用。

  5. Router 的作用是什么?它自动生成了哪些 URL?

    1. 在 Django REST framework(DRF)中,Router 是一个非常重要的组件,它的主要作用是:自动为 ViewSet 生成 URL 配置,从而大幅简化 RESTful API 的路由管理,避免手动编写重复的 URL 模式。


      一、Router 的作用是什么?

      ✅ 核心作用:

      1. 自动注册 URL 路由

        将 ViewSet 中定义的操作(如 list、create、retrieve、update、destroy)自动映射到对应的 URL。

      2. 减少样板代码

        无需手动为每个视图函数写 path()url(),提升开发效率。

      3. 统一 URL 风格

        遵循 RESTful 规范,自动生成标准化的 URL 路径(如 /api/users/, /api/users/{id}/)。

      4. 支持自定义动作(@action)

        自动为通过 @action装饰器添加的自定义方法生成 URL。

      5. 与 ViewSet 深度集成

        必须与 ViewSet(或其子类如 ModelViewSet)配合使用。


      二、常见的 Router 类型

      DRF 提供两种常用 Router:

      Router 类 说明
      SimpleRouter 基础路由器,不包含根视图(API Root)
      DefaultRouter 继承自 SimpleRouter,额外提供一个可浏览的 API 根页面(如 /api/

      通常使用 DefaultRouter,因为它更友好(带 HTML 文档页)。


      三、Router 自动生成哪些 URL?

      当你使用 router.register()注册一个 ViewSet 时,DRF 会根据 ViewSet 提供的动作(actions)自动生成一组 URL。

      示例:注册一个 UserViewSet

      # views.py
      from rest_framework import viewsets
      from .models import User
      from .serializers import UserSerializer
      
      class UserViewSet(viewsets.ModelViewSet):
          queryset = User.objects.all()
          serializer_class = UserSerializer
      # urls.py
      from rest_framework.routers import DefaultRouter
      from .views import UserViewSet
      
      router = DefaultRouter()
      router.register(r'users', UserViewSet)  # 注册路由
      
      urlpatterns = router.urls
      

      🎯 自动生成的 URL 模式(以 users为例):

      HTTP 方法 URL 路径 对应 ViewSet 方法 说明
      GET /users/ .list() 获取用户列表
      POST /users/ .create() 创建新用户
      GET /users/{id}/ .retrieve() 获取单个用户详情
      PUT /users/{id}/ .update() 全量更新用户信息
      PATCH /users/{id}/ .partial_update() 部分更新用户信息
      DELETE /users/{id}/ .destroy() 删除用户

      ✅ 这些 URL 全部由 router.register()自动生成,无需手写!


      四、支持自定义动作时的 URL 生成

      如果在 ViewSet 中使用 @action装饰器添加自定义方法,Router 也会自动为其生成 URL。

      示例:添加自定义动作

      from rest_framework.decorators import action
      from rest_framework.response import Response
      
      class UserViewSet(viewsets.ModelViewSet):
          queryset = User.objects.all()
          serializer_class = UserSerializer
      
          @action(detail=False, methods=['get'])
          def recent(self, request):
              """获取最近注册的5个用户"""
              recent_users = User.objects.order_by('-date_joined')[:5]
              serializer = self.get_serializer(recent_users, many=True)
              return Response(serializer.data)
      
          @action(detail=True, methods=['post'])
          def set_inactive(self, request, pk=None):
              """将指定用户设为非活跃状态"""
              user = self.get_object()
              user.is_active = False
              user.save()
              return Response({'status': 'user deactivated'})
      

      🎯 自动新增的 URL:

      HTTP 方法 URL 路径 对应方法 说明
      GET /users/recent/ .recent() 自定义集合动作(detail=False)
      POST /users/{id}/set_inactive/ .set_inactive() 自定义详情动作(detail=True)

      🔍 注意:

      • detail=False:动作作用于整个资源集合,URL 不带 ID。
      • detail=True:动作作用于单个资源,URL 包含 {id}

      五、DefaultRouter 还会生成一个 API Root 页面

      使用 DefaultRouter时,除了资源路由外,还会自动添加一个根视图:

      URL 说明
      //api/(取决于注册前缀) 显示所有注册资源的链接(HTML 页面),方便调试和浏览

      访问 /api/会看到类似这样的页面:

      Users
        • http://localhost:8000/api/users/
        • http://localhost:8000/api/users/{id}/
      

      六、总结

      ✅ Router 的作用总结:

      • 自动为 ViewSet 生成 RESTful 风格的 URL。
      • 支持标准 CRUD 操作和自定义动作。
      • 减少手动配置 URL 的工作量。
      • 提升 API 的一致性和可维护性。

      🧩 自动生成的典型 URL(以 users为例):

      GET, POST     /users/           → list(), create()
      GET, PUT, PATCH, DELETE /users/{id}/ → retrieve(), update(), partial_update(), destroy()
      GET           /users/recent/     → @action(detail=False)
      POST          /users/{id}/set_inactive/ → @action(detail=True)
      

      💡 最佳实践:

      • 使用 DefaultRouter以获得更好的开发体验(含 API 根页面)。
      • 始终将 ViewSet 与 Router 配合使用,除非有特殊需求。
      • 利用 @action扩展非标准操作,保持 URL 整洁统一。

      🚀 Router 是 DRF 实现“约定优于配置”(Convention over Configuration)哲学的关键体现!

  6. Generic Views(如 ListCreateAPIView)的优势和使用场景?

    1. 在 Django REST framework(DRF)中,Generic Views(通用视图) 是一组预定义的基于类的视图,它们封装了常见的 API 开发模式(如列表展示、详情查看、创建、更新、删除等)。像 ListCreateAPIViewRetrieveUpdateDestroyAPIView等都是 Generic Views 的具体实现。


      一、什么是 Generic Views?

      Generic Views 继承自 APIView,并在此基础上结合了 mixin类(如 ListModelMixinCreateModelMixin等),为常见的 RESTful 操作提供了开箱即用的实现。

      常见的 Generic Views:

      类名 功能描述
      ListAPIView 仅支持列表查询(GET)
      CreateAPIView 仅支持创建(POST)
      RetrieveAPIView 仅支持获取详情(GET)
      UpdateAPIView 支持更新(PUT、PATCH)
      DestroyAPIView 支持删除(DELETE)
      ListCreateAPIView 支持列表 + 创建(GET + POST)✅ 常用
      RetrieveUpdateAPIView 支持详情 + 更新(GET + PUT/PATCH)
      RetrieveDestroyAPIView 支持详情 + 删除(GET + DELETE)
      RetrieveUpdateDestroyAPIView 支持详情 + 更新 + 删除(GET + PUT/PATCH + DELETE)✅ 常用

      二、Generic Views 的优势

      1. 代码复用性强

      • 封装了通用的 CRUD 逻辑,避免重复编写 get(), post()等方法。
      • 开发者只需关注数据来源(queryset)和序列化方式(serializer_class)。

      2. 开发效率高

      • 几行代码即可搭建完整的 API 端点。
      • 无需手动实现 list(), create()等方法的细节。

      3. 一致性好

      • 所有基于 Generic Views 的接口行为一致,便于维护和团队协作。

      4. 易于扩展

      • 可通过重写方法(如 get_queryset()perform_create())实现自定义逻辑。
      • 支持添加权限、分页、过滤等功能。

      5. 与 DRF 生态无缝集成

      • 自动支持分页、过滤、排序、限流等高级特性(通过 filter_backends等配置)。

      三、使用示例:ListCreateAPIView

      场景:实现一个用户列表和创建用户的接口

      # views.py
      from rest_framework.generics import ListCreateAPIView
      from .models import User
      from .serializers import UserSerializer
      
      class UserListCreateView(ListCreateAPIView):
          queryset = User.objects.all()
          serializer_class = UserSerializer
      
          # 可选:自定义查询集
          def get_queryset(self):
              return User.objects.filter(is_active=True)
      
          # 可选:创建前预处理
          def perform_create(self, serializer):
              serializer.save(created_by=self.request.user)
      

      对应 URL 配置:

      # urls.py
      from django.urls import path
      from .views import UserListCreateView
      
      urlpatterns = [
          path('users/', UserListCreateView.as_view(), name='user-list-create'),
      ]
      

      自动支持的接口:

      方法 URL 功能
      GET /users/ 获取所有用户列表(支持分页)
      POST /users/ 创建新用户

      ✅ 无需手动写 get()post()方法!


      四、使用场景

      ✅ 适合使用 Generic Views 的场景:

      1. 标准的 CRUD 操作

        如对单个模型进行列表、详情、创建、更新、删除。

      2. 快速原型开发

        需要在短时间内搭建可用的 API。

      3. 前后端分离项目的基础接口

        如管理后台的用户、文章、订单等资源接口。

      4. 不需要复杂自定义逻辑的视图

        业务逻辑可以直接通过配置或少量重写完成。

      5. 希望统一视图风格

        团队项目中保持代码风格一致。


      ❌ 不适合使用 Generic Views 的场景:

      1. 高度定制化的业务逻辑

        如一个接口需要聚合多个模型、调用外部服务、复杂权限判断等。

      2. 非 RESTful 接口

        如文件上传、Webhook、批量操作等不符合标准 CRUD 的模式。

      3. 需要完全控制请求/响应流程

        此时应使用 APIViewView

      4. 动态 URL 结构或非常规方法分发

        如根据参数决定执行不同操作。

      📌 提示:如果 Generic Views 无法满足需求,可以考虑:

      • 继承 Generic Views 并重写方法;
      • 改用 APIView
      • 使用 ViewSet+ Router(更适合资源型 API)。

      五、Generic Views vs ViewSet 对比

      特性 Generic Views ViewSet
      配置方式 手动绑定 URL(as_view() 自动注册(router.register()
      适用粒度 单个 HTTP 方法组合(如 GET+POST) 整个资源操作集合
      URL 灵活性 高(可自由定义路径) 标准化(RESTful)
      开发速度 更快(尤其配合 Router)
      适用场景 简单或中等复杂度的接口 标准 RESTful 资源 API

      💡 建议:

      • 如果只需要一两个动作(如列表+创建),用 ListCreateAPIView更直观。
      • 如果需要完整的 CRUD 或自定义动作,用 ModelViewSet+ Router 更高效。

      六、总结

      ✅ Generic Views 的优势:

      • 提高开发效率,减少样板代码
      • 内置分页、过滤等支持
      • 易于理解和维护
      • 适合快速构建标准化 API

      🎯 推荐使用场景:

      当你需要为一个模型快速实现标准的“列表 + 创建”或“详情 + 更新 + 删除”接口时,ListCreateAPIViewRetrieveUpdateDestroyAPIView是最佳选择。

      📌 示例代码回顾:

      class ArticleListCreateView(ListCreateAPIView):
          queryset = Article.objects.all()
          serializer_class = ArticleSerializer
          permission_classes = [IsAuthenticatedOrReadOnly]
      
          def perform_create(self, serializer):
              serializer.save(author=self.request.user)
      

      一行 as_view()就能上线一个功能完善的接口!

      🚀 善用 Generic Views,让 DRF 开发事半功倍!

  7. DRF 中如何实现分页(Pagination)?有哪几种内置分页类?

    1. 在 Django REST framework(DRF)中,分页(Pagination) 是一种重要的机制,用于控制 API 响应中返回的数据量,避免一次性返回过多数据导致性能问题或带宽浪费。DRF 提供了灵活的分页支持,既可以在全局配置,也可以在单个视图中自定义。


      一、为什么需要分页?

      • 防止一次性返回大量数据(如数万条记录),导致:
        • 响应缓慢
        • 内存占用过高
        • 前端处理困难
      • 提升用户体验和 API 性能
      • 支持按需加载(如无限滚动)

      二、DRF 中如何实现分页?

      DRF 实现分页的核心步骤:

      步骤 1:配置分页类

      选择一个分页类(内置或自定义),并设置相关参数。

      步骤 2:启用分页

      • 全局启用:在 settings.py中配置 DEFAULT_PAGINATION_CLASSPAGE_SIZE
      • 局部启用:在视图类中单独指定 pagination_class

      步骤 3:访问分页数据

      客户端通过 ?page=参数请求特定页码(如 ?page=2


      三、DRF 的内置分页类(3 种)

      DRF 提供了三种内置分页类,位于 rest_framework.pagination模块中:

      分页类 说明 响应格式特点
      PageNumberPagination 基于页码的分页(最常用) 返回当前页数据和页码信息
      LimitOffsetPagination 基于偏移量的分页(类似 SQL LIMIT/OFFSET) 返回起始位置和数量
      CursorPagination 基于游标的分页(高效、防跳页) 返回下一页游标,适合大数据量

      1. PageNumberPagination(页码分页)

      ✅ 特点:

      • 通过 ?page=指定页码(如 ?page=3
      • 可配置每页数量 ?page_size=20

      🔧 全局配置(settings.py):

      REST_FRAMEWORK = {
          'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
          'PAGE_SIZE': 10  # 默认每页 10 条
      }
      

      🔧 局部配置(视图中):

      from rest_framework.pagination import PageNumberPagination
      from rest_framework.generics import ListAPIView
      from .models import Book
      from .serializers import BookSerializer
      
      class StandardResultsSetPagination(PageNumberPagination):
          page_size = 5
          page_size_query_param = 'page_size'  # 允许客户端指定每页大小
          max_page_size = 100                 # 限制最大每页数量
      
      class BookListView(ListAPIView):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          pagination_class = StandardResultsSetPagination
      

      📦 响应示例:

      {
          "count": 45,
          "next": "http://api.example.com/books/?page=2",
          "previous": null,
          "results": [
              {"id": 1, "title": "Book A"},
              {"id": 2, "title": "Book B"},
              ...
          ]
      }
      

      字段说明:

      • count: 总记录数
      • next: 下一页 URL(若无则为 null
      • previous: 上一页 URL
      • results: 当前页数据列表

      2. LimitOffsetPagination(偏移量分页)

      ✅ 特点:

      • 使用 ?limit=?offset=控制(如 ?limit=10&offset=20
      • 类似数据库 LIMIT 10 OFFSET 20

      🔧 使用示例:

      from rest_framework.pagination import LimitOffsetPagination
      
      class BookListView(ListAPIView):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          pagination_class = LimitOffsetPagination
      

      📦 响应示例:

      {
          "count": 45,
          "next": "http://api.example.com/books/?limit=10&offset=30",
          "previous": "http://api.example.com/books/?limit=10&offset=10",
          "results": [...]
      }
      

      💡 适合需要“从第 N 条开始取 M 条”的场景,如搜索结果跳转。


      3. CursorPagination(游标分页)

      ✅ 特点:

      • 使用“游标”(cursor)定位下一页,不支持跳转到任意页
      • 高性能,适合无限滚动实时数据流
      • 避免“跳页”导致的漏数据或重复数据(如数据在分页期间被修改)

      🔧 使用示例:

      from rest_framework.pagination import CursorPagination
      
      class BookCursorPagination(CursorPagination):
          page_size = 10
          ordering = '-created_at'  # 必须指定排序字段(通常是时间戳或 ID)
          cursor_query_param = 'cursor'
          page_size_query_param = 'page_size'
      
      class BookListView(ListAPIView):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          pagination_class = BookCursorPagination
      

      📦 响应示例:

      {
          "next": "http://api.example.com/books/?cursor=cD0yMDIzLTAxLTE1JmNv%3D",
          "previous": null,
          "results": [...]
      }
      

      ⚠️ 注意:

      • 必须设置 ordering,否则报错
      • 不支持 page参数,只能通过 next/previous链接翻页
      • 无法跳转到第 5 页,只能“上一页/下一页”

      四、如何禁用分页?

      方法 1:全局禁用

      REST_FRAMEWORK = {
          'DEFAULT_PAGINATION_CLASS': None
      }
      

      方法 2:视图中禁用

      class BookListView(ListAPIView):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          pagination_class = None  # 禁用分页
      

      方法 3:单个请求禁用

      某些 Generic Views 支持 ?disable_pagination=true(需自定义),但默认不支持。通常做法是:

      • 提供另一个无分页的视图
      • 或使用 django.core.paginator.Paginator手动控制

      五、自定义分页类

      你可以继承任一内置分页类,实现完全自定义:

      class CustomPagination(PageNumberPagination):
          page_size = 8
          page_query_param = 'page'  # 默认就是 'page'
          page_size_query_param = 'size'
          max_page_size = 50
      
          def get_paginated_response(self, data):
              return Response({
                  'total': self.page.paginator.count,
                  'pages': self.page.paginator.num_pages,
                  'current_page': self.page.number,
                  'items': data
              })
      

      六、总结

      分页类 适用场景 是否支持跳页 性能 推荐指数
      PageNumberPagination 常规列表页(如后台管理) ✅ 支持 一般 ⭐⭐⭐⭐
      LimitOffsetPagination 搜索、筛选、API 灵活查询 ✅ 支持 一般 ⭐⭐⭐
      CursorPagination 无限滚动、实时数据、大数据量 ❌ 不支持 ⭐⭐⭐⭐⭐(特定场景)

      ✅ 最佳实践建议:

      • 普通后台管理系统 → 使用 PageNumberPagination
      • 移动端无限滚动 → 使用 CursorPagination
      • 需要精确控制查询位置 → 使用 LimitOffsetPagination
      • 始终设置合理的 PAGE_SIZEmax_page_size防止滥用

      🚀 合理使用分页,是构建高性能、可扩展 API 的关键一步!

  8. DRF 的认证方式有哪几种?如何实现 Token 认证和 JWT 认证?

    1. 在 Django REST framework(DRF)中,认证(Authentication) 是确定“谁在访问 API”的核心机制。DRF 提供了灵活的认证系统,支持多种认证方式,并允许你根据需求组合或自定义认证后端。


      一、DRF 的内置认证方式

      DRF 默认支持以下几种认证类(位于 rest_framework.authentication):

      认证类 说明
      SessionAuthentication 使用 Django 的 session 机制(适用于浏览器 + 登录状态)
      BasicAuthentication HTTP 基础认证(用户名+密码明文传输,仅用于测试)
      TokenAuthentication 基于 Token 的字符串认证(DRF 自带,较简单)
      JSONWebTokenAuthentication(第三方) 基于 JWT(JSON Web Token)的现代认证方式(需安装 djangorestframework-simplejwt

      ✅ 你可以在全局或视图级别配置使用哪些认证方式。


      二、认证的配置方式

      1. 全局配置(推荐)

      settings.py中配置:

      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': [
              'rest_framework.authentication.SessionAuthentication',
              'rest_framework.authentication.BasicAuthentication',
              # 'rest_framework.authentication.TokenAuthentication',  # 启用 Token
              # 'rest_framework_simplejwt.authentication.JWTAuthentication',  # 启用 JWT
          ],
      }
      

      2. 局部配置(视图或视图集)

      from rest_framework.authentication import TokenAuthentication
      from rest_framework.permissions import IsAuthenticated
      from rest_framework.views import APIView
      
      class ProtectedView(APIView):
          authentication_classes = [TokenAuthentication]
          permission_classes = [IsAuthenticated]
      
          def get(self, request):
              return Response({"message": f"Hello, {request.user.username}!"})
      

      三、实现 Token 认证(DRF 自带)

      步骤 1:启用 TokenAuthentication

      # settings.py
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': [
              'rest_framework.authentication.TokenAuthentication',
          ],
          'DEFAULT_PERMISSION_CLASSES': [
              'rest_framework.permissions.IsAuthenticated',  # 保护所有接口
          ],
      }
      

      步骤 2:迁移数据库(创建 token 表)

      python manage.py migrate
      

      DRF 会自动创建 authtoken_token表用于存储 token。

      步骤 3:为用户创建 Token

      方法 1:手动创建(Django shell)

      python manage.py shell
      from django.contrib.auth.models import User
      from rest_framework.authtoken.models import Token
      
      user = User.objects.get(username='alice')
      token = Token.objects.create(user=user)
      print(token.key)  # 输出:a1b2c3d4e5f6...
      

      方法 2:自动创建(用户注册时生成)

      from django.conf import settings
      from rest_framework.authtoken.models import Token
      
      @receiver(post_save, sender=settings.AUTH_USER_MODEL)
      def create_auth_token(sender, instance=None, created=False, **kwargs):
          if created:
              Token.objects.create(user=instance)
      

      添加到 signals.py并在 apps.py中注册。

      步骤 4:客户端使用 Token 访问 API

      在请求头中添加:

      Authorization: Token a1b2c3d4e5f6...
      

      或使用 curl:

      curl -H "Authorization: Token a1b2c3d4e5f6..." http://localhost:8000/api/protected/
      

      四、实现 JWT 认证(推荐用于现代应用)

      JWT(JSON Web Token)是目前最流行的无状态认证方式,适合前后端分离、移动端、微服务架构。

      步骤 1:安装依赖

      pip install djangorestframework-simplejwt
      

      步骤 2:配置 JWT 认证

      # settings.py
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': (
              'rest_framework_simplejwt.authentication.JWTAuthentication',
          ),
      }
      
      # JWT 配置(可选)
      from datetime import timedelta
      
      SIMPLE_JWT = {
          'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),      # Access Token 有效期
          'REFRESH_TOKEN_LIFETIME': timedelta(days=1),         # Refresh Token 有效期
          'ROTATE_REFRESH_TOKENS': False,
          'BLACKLIST_AFTER_ROTATION': True,
      }
      

      步骤 3:配置 URL 路由

      # urls.py
      from django.urls import path
      from rest_framework_simplejwt.views import (
          TokenObtainPairView,
          TokenRefreshView,
      )
      
      urlpatterns = [
          path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
          path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
      ]
      

      TokenObtainPairView:登录时获取 access 和 refresh token

      TokenRefreshView:用 refresh token 刷新 access token

      步骤 4:登录获取 Token

      发送 POST 请求:

      POST /api/token/
      {
          "username": "alice",
          "password": "123456"
      }
      

      响应:

      {
          "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
          "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      }
      

      步骤 5:使用 Access Token 访问受保护接口

      在请求头中添加:

      Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
      

      注意:Bearer后面有一个空格!

      curl 示例:

      curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." http://localhost:8000/api/user/
      

      步骤 6:(可选)启用 Token 黑名单(推荐)

      pip install djangorestframework-simplejwt[token_blacklist]
      # settings.py
      INSTALLED_APPS += ['rest_framework_simplejwt.token_blacklist']
      
      # 运行迁移
      python manage.py migrate
      

      然后配置:

      SIMPLE_JWT = {
          'ROTATE_REFRESH_TOKENS': True,
          'BLACKLIST_AFTER_ROTATION': True,
      }
      

      添加登出接口(撤销 refresh token):

      # urls.py
      from rest_framework_simplejwt.views import TokenBlacklistView
      
      urlpatterns += [
          path('api/token/blacklist/', TokenBlacklistView.as_view(), name='token_blacklist'),
      ]
      

      五、Token 认证 vs JWT 认证对比

      特性 Token Authentication JWT Authentication
      存储位置 服务端数据库(authtoken_token表) 客户端(本地存储或内存)
      状态性 有状态(服务端跟踪 token) 无状态(服务端不存 token)
      扩展性 差(难扩展 payload) 好(可自定义 claims)
      安全性 一般(固定字符串) 高(可加密、过期、签名)
      移动端支持 一般 优秀
      注销/踢人 需手动删 token 需配合黑名单(如 Redis)
      推荐场景 内部系统、简单项目 前后端分离、APP、微服务

      推荐:新项目优先使用 JWT(如 SimpleJWT)


      六、总结

      ✅ DRF 支持的主要认证方式:

      1. SessionAuthentication– 浏览器会话(传统 Web)
      2. BasicAuthentication– 测试用(不安全)
      3. TokenAuthentication– DRF 自带,简单但有状态
      4. JWTAuthentication(via djangorestframework-simplejwt)– 现代首选,无状态、安全、灵活

      🛠️ 实现要点:

      认证方式 关键步骤
      Token 启用类 → 迁移 → 创建 token → Authorization: Token xxx
      JWT 安装 SimpleJWT → 配置 → 路由 → 登录获取 token → Authorization: Bearer xxx

      💡 最佳实践:

      • 生产环境避免使用 BasicAuthentication
      • 前后端分离项目推荐使用 JWT + HTTPS
      • 敏感操作建议结合权限(IsAuthenticatedIsAdminUser等)
      • 定期轮换密钥,设置合理的 token 有效期

      🚀 选择合适的认证方式,是保障 API 安全和用户体验的关键!

  9. 权限类(Permission Classes)有哪些内置类型?如何自定义权限?

    1. 在 Django REST framework(DRF)中,权限类(Permission Classes) 用于控制用户是否有权限访问某个 API 视图或执行某个操作。它是保障 API 安全性的核心机制之一。


      一、DRF 内置权限类(Built-in Permission Classes)

      DRF 提供了多个内置权限类,位于 rest_framework.permissions模块中:

      权限类 说明
      AllowAny 允许任何用户访问(包括匿名用户)
      IsAuthenticated 仅允许已认证的用户访问
      IsAdminUser 仅允许 is_staff=True的管理员用户访问
      IsAuthenticatedOrReadOnly 已认证用户可读写,匿名用户只读(GET/HEAD/OPTIONS)
      DjangoModelPermissions 基于 Django 模型的权限系统(需配合 Django 的 auth 权限)
      DjangoModelPermissionsOrAnonReadOnly 类似 DjangoModelPermissions,但允许匿名用户只读
      DjangoObjectPermissions 支持对象级别的权限控制(需配合 django-guardian等)

      1. AllowAny

      • 用途:公开接口,如首页、登录、文档等。

      • 示例

        from rest_framework.permissions import AllowAny
        
        class PublicView(APIView):
            permission_classes = [AllowAny]
        

      2. IsAuthenticated

      • 用途:需要登录才能访问,如用户中心、个人资料。

      • 示例

        from rest_framework.permissions import IsAuthenticated
        
        class ProfileView(APIView):
            permission_classes = [IsAuthenticated]
        

      3. IsAdminUser

      • 用途:仅管理员可访问,如后台管理接口。

      • 示例

        from rest_framework.permissions import IsAdminUser
        
        class AdminOnlyView(APIView):
            permission_classes = [IsAdminUser]
        

      4. IsAuthenticatedOrReadOnly

      • 用途:登录用户可读写,游客只能查看。

      • 常用于:博客、商品列表等开放但需保护的场景。

      • 示例

        from rest_framework.permissions import IsAuthenticatedOrReadOnly
        
        class ArticleListView(ListCreateAPIView):
            permission_classes = [IsAuthenticatedOrReadOnly]
        

      5. DjangoModelPermissions

      • 用途:基于 Django 的 add/change/delete/view权限。

      • 要求:必须在模型中设置权限(默认已开启)。

      • 示例

        from rest_framework.permissions import DjangoModelPermissions
        
        class ArticleViewSet(ModelViewSet):
            queryset = Article.objects.all()
            serializer_class = ArticleSerializer
            permission_classes = [DjangoModelPermissions]  # 需有对应权限才能 POST/PUT/DELETE
        

        用户必须拥有 app.add_articleapp.change_article等权限才能操作。

      6. DjangoObjectPermissions

      • 用途:细粒度对象级权限(如“只能编辑自己发布的文章”)。
      • 依赖:需安装 django-guardian并实现 ObjectPermissionBackend
      • 使用较少,适合复杂权限系统。

      二、权限的配置方式

      1. 全局配置(settings.py)

      REST_FRAMEWORK = {
          'DEFAULT_PERMISSION_CLASSES': [
              'rest_framework.permissions.IsAuthenticated',  # 默认所有接口需登录
          ]
      }
      

      2. 局部配置(视图或视图集)

      from rest_framework.permissions import IsAdminUser
      
      class UserManagementView(APIView):
          permission_classes = [IsAdminUser]  # 覆盖全局设置
      

      3. 动态设置(在方法中)

      def get_permissions(self):
          if self.action == 'create':
              return [IsAdminUser()]
          elif self.action == 'list':
              return [AllowAny()]
          return [IsAuthenticated()]
      

      适用于 ViewSet,可根据动作动态调整权限。


      三、如何自定义权限类?

      当内置权限无法满足需求时(如“VIP 用户才能访问”、“作者本人才能编辑”),可以自定义权限类。

      ✅ 自定义权限类的要求:

      继承 BasePermission

      实现 has_permission()(控制是否允许访问视图)

      或实现 has_object_permission()(控制是否允许操作某个对象)


      示例 1:只允许 VIP 用户访问

      # permissions.py
      from rest_framework import permissions
      
      class IsVIPUser(permissions.BasePermission):
          """
          仅允许 is_vip=True 的用户访问
          """
      
          def has_permission(self, request, view):
              return request.user and request.user.is_authenticated and getattr(request.user, 'is_vip', False)
      

      使用:

      from .permissions import IsVIPUser
      
      class PremiumContentView(APIView):
          permission_classes = [IsVIPUser]
      

      示例 2:只有作者本人才能编辑文章(对象级权限)

      # permissions.py
      class IsAuthorOrReadOnly(permissions.BasePermission):
          """
          作者可读写,其他人只读
          """
      
          def has_object_permission(self, request, view, obj):
              # 读取权限允许所有用户
              if request.method in permissions.SAFE_METHODS:  # GET, HEAD, OPTIONS
                  return True
      
              # 写入权限只允许作者
              return obj.author == request.user
      

      使用(在 RetrieveUpdateDestroyAPIViewModelViewSet中):

      class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView):
          queryset = Article.objects.all()
          serializer_class = ArticleSerializer
          permission_classes = [IsAuthorOrReadOnly]
      

      ⚠️ has_object_permission只在视图返回单个对象时调用(如 retrieve, update, destroy),不会在 list中调用。


      示例 3:组合多个权限(AND 逻辑)

      DRF 会自动组合多个权限类(相当于“与”关系):

      permission_classes = [IsAuthenticated, IsVIPUser]  # 必须同时满足
      

      等价于:

      class ComposedPermission(permissions.BasePermission):
          def has_permission(self, request, view):
              return (
                  permissions.IsAuthenticated().has_permission(request, view) and
                  IsVIPUser().has_permission(request, view)
              )
      

      四、权限类方法详解

      方法 调用时机 返回值 说明
      has_permission(request, view) 每次请求进入视图时调用 True/False 控制是否允许访问该视图
      has_object_permission(request, view, obj) 视图返回单个对象时调用(如 get_object() True/False 控制是否允许操作该对象

      ✅ 如果 has_permission返回 False,直接返回 403,不会调用 has_object_permission


      五、总结

      ✅ 内置权限类速查:

      权限类 适用场景
      AllowAny 公开接口
      IsAuthenticated 需登录
      IsAdminUser 仅管理员
      IsAuthenticatedOrReadOnly 登录读写,游客只读
      DjangoModelPermissions 基于模型权限
      DjangoObjectPermissions 对象级权限(需扩展)

      🛠️ 自定义权限步骤:

      1. 继承 permissions.BasePermission
      2. 实现 has_permission()has_object_permission()
      3. 返回 True(允许)或 False(拒绝)
      4. 在视图中引用自定义类

      💡 最佳实践:

      • 遵循“最小权限原则”:默认禁止,按需开放
      • 敏感操作(如删除、支付)应叠加多重验证(权限 + 业务逻辑)
      • 使用 SAFE_METHODS区分读写操作
      • 复杂权限建议封装为独立类,便于复用和测试

      🚀 合理设计权限系统,是保障 API 安全性和用户体验的基石!

  10. 节流(Throttling)的作用是什么?举例说明限制某 IP 访问频率的方法。

    1. 在 Django REST framework(DRF)中,节流(Throttling) 是一种流量控制机制,用于限制用户或客户端在特定时间内的请求次数,防止 API 被滥用、恶意攻击(如暴力破解、爬虫、DDoS)或意外过载。


      一、节流(Throttling)的作用

      ✅ 核心作用:

      1. 防止滥用:避免单个用户/IP 高频调用接口(如短信发送、登录尝试)。
      2. 保护服务器资源:防止因突发流量导致服务崩溃。
      3. 提升系统稳定性:平滑请求负载,避免峰值冲击。
      4. 实现公平使用:防止少数用户占用过多资源。
      5. 安全防护:抵御暴力破解、刷单、爬虫等行为。

      📌 类比:就像高速公路的“限速”或景区的“人流管控”。


      二、DRF 中的节流机制

      DRF 提供了灵活的节流系统,支持:

      • 按用户、IP、自定义标识限流
      • 不同视图设置不同限流策略
      • 全局或局部配置
      • 自定义节流类

      内置节流类(rest_framework.throttling):

      节流类 说明
      AnonRateThrottle 限制匿名用户(按 IP)
      UserRateThrottle 限制已认证用户(按用户 ID)
      ScopedRateThrottle 按自定义作用域(scope)限流,支持不同接口不同策略
      SimpleRateThrottle 所有节流类的基类,可自定义逻辑

      三、举例:限制某 IP 的访问频率

      场景:限制匿名用户(按 IP)每分钟最多访问 5 次 /api/login/

      步骤 1:全局启用节流(可选)

      # settings.py
      REST_FRAMEWORK = {
          'DEFAULT_THROTTLE_CLASSES': [
              'rest_framework.throttling.AnonRateThrottle',  # 匿名用户按 IP 限流
              'rest_framework.throttling.UserRateThrottle'   # 登录用户按用户限流
          ],
          'DEFAULT_THROTTLE_RATES': {
              'anon': '5/min',      # 匿名用户:每分钟 5 次
              'user': '100/day'     # 登录用户:每天 100 次
          }
      }
      

      '5/min' 表示每分钟最多 5 次请求。
      支持单位:/sec(秒)、/min(分)、/hour(小时)、/day(天)


      步骤 2:在视图中单独设置节流(推荐用于精细控制)

      如果不想全局生效,可以在特定视图中设置:

      # views.py
      from rest_framework.throttling import AnonRateThrottle
      from rest_framework.views import APIView
      from rest_framework.response import Response
      
      class LoginView(APIView):
          throttle_classes = [AnonRateThrottle]  # 只对这个视图启用 IP 限流
      
          def post(self, request):
              # 登录逻辑
              return Response({"message": "Login successful"})
      

      ⚠️ 注意:要使 AnonRateThrottle 生效,需在 settings.py 中配置 DEFAULT_THROTTLE_RATES 包含 'anon'


      步骤 3:自定义节流类(更灵活的控制)

      如果你想针对 /api/login/ 设置更严格的限制(如每分钟 3 次),可以自定义节流类:

      # throttles.py
      from rest_framework.throttling import AnonRateThrottle
      
      class LoginAttemptThrottle(AnonRateThrottle):
          scope = 'login_attempts'  # 自定义作用域名称
      
      # settings.py
      REST_FRAMEWORK = {
          'DEFAULT_THROTTLE_RATES': {
              'anon': '5/min',
              'user': '100/day',
              'login_attempts': '3/min',  # 为 login_attempts 设置独立速率
          }
      }
      
      # views.py
      from .throttles import LoginAttemptThrottle
      
      class LoginView(APIView):
          throttle_classes = [LoginAttemptThrottle]  # 使用自定义节流
      
          def post(self, request):
              # 登录逻辑
              return Response({"message": "Login attempt recorded"})
      

      四、测试节流效果

      使用 curl 模拟多次请求:

      # 连续发送 6 次请求(超过 5 次/分钟)
      for i in {1..6}; do
        curl -X POST http://localhost:8000/api/login/ \
             -H "Content-Type: application/json" \
             -d '{"username":"test","password":"123"}'
      done
      

      第 6 次请求将返回:

      {
        "detail": "Request was throttled. Expected available in 59 seconds."
      }
      

      状态码:429 Too Many Requests


      五、查看剩余请求次数(可选)

      DRF 会在响应头中返回节流信息(如果配置了 THROTTLE_RATES):

      X-Throttle-Wait-Seconds: 59
      Retry-After: 59
      

      你也可以通过 @property 自定义响应提示。


      六、其他实用技巧

      1. 只对特定方法限流

      class LoginView(APIView):
          throttle_classes = [LoginAttemptThrottle]
      
          def post(self, request):
              ...
      
          def get(self, request):
              return Response({"info": "This is a public endpoint"})  # 不受限流影响?
      

      ⚠️ 默认整个视图都受限流。若只想对 POST 限流,需自定义逻辑或使用 @action 拆分视图。

      2. 白名单用户(如管理员不限流)

      class CustomAnonThrottle(AnonRateThrottle):
          def allow_request(self, request, view):
              # 管理员 IP 或特定条件不限流
              if request.META.get('REMOTE_ADDR') in ['127.0.0.1', '192.168.1.100']:
                  return True
              return super().allow_request(request, view)
      

      七、总结

      ✅ 节流的作用:

      • 防止 API 被滥用或攻击
      • 控制资源使用,提升稳定性
      • 实现公平访问策略

      🎯 限制 IP 访问频率的方法:

      方法 说明
      使用 AnonRateThrottle 按 IP 限制匿名用户
      配置 DEFAULT_THROTTLE_RATES 设置 'anon': '5/min' 等规则
      局部设置 throttle_classes 精确控制某个视图
      自定义节流类 实现更细粒度的策略(如登录接口单独限流)

      💡 最佳实践:

      • 登录、注册、发送验证码等敏感接口应严格限流(如 3~5 次/分钟)
      • 结合日志监控异常请求模式
      • 对 VIP 用户可提高限额或不限流
      • 使用 ScopedRateThrottle 实现多接口差异化策略

      🚀 合理使用节流,是构建安全、稳定、高可用 API 的重要保障!

posted @ 2025-12-09 18:50  GDms  阅读(20)  评论(0)    收藏  举报