eagleye

DRF视图集自定义方法@action与request对象详解

DRF视图集自定义方法@action与request对象详解

一、@action装饰器基础配置解析

1.1 装饰器参数说明

@action(detail=False, methods=['post'], url_path='batch')

def bulk_upload(self, request):

"""

批量上传自定义端点

- detail=False: 作用于资源集合,URL路径为/resource/batch/

- methods=['post']: 仅接受POST请求

- url_path='batch': 自定义URL路径段,默认使用方法名

"""

  • detail参数False表示操作整个资源集合(如/users/batch/),True表示操作单个实例(如/users/{id}/activate/)[^2][^3]
  • methods参数:显式声明允许的HTTP方法,未指定则默认支持GET,企业级开发必须显式指定以避免安全风险[^7]
  • url_path参数:自定义URL路径,支持中划线命名(如url_path='batch-upload'),提升API可读性[^4]

1.2 路由与访问方式

DRF路由器自动生成路由规则:

# urls.py

from rest_framework.routers import DefaultRouter

router = DefaultRouter()

router.register(r'resources', ResourceViewSet)

# 自动注册路由: POST /resources/batch/ → bulk_upload方法

  • 无需手动配置URL,降低维护成本
  • 支持通过url_name参数自定义路由名称,便于反向解析[^7]
  • 功能:自动解析JSON、表单、文件等格式的请求体数据,替代Django的request.POST和request.FILES[^5][^18]
  • 企业级用法

二、request对象核心属性与企业级应用

2.1 请求数据获取

2.1.1 request.data(请求体数据)

def bulk_upload(self, request):

# 安全获取批量数据(自动解析Content-Type)

upload_data = request.data.get('items', [])

# 文件处理(支持multipart/form-data格式)

files = request.FILES.getlist('files') # 获取多文件列表

# 数据验证(企业级必须步骤)

serializer = BulkUploadSerializer(data=upload_data)

if not serializer.is_valid():

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

2.1.2 request.query_params(URL查询参数)

  • 功能:获取URL中的查询字符串参数,等同于Django的request.GET,命名更符合REST规范[^8][^15]
  • 企业级用法

def bulk_upload(self, request):

# 分页参数获取(防SQL注入)

page = request.query_params.get('page', 1)

# 过滤条件获取

filter_type = request.query_params.get('filter', 'all')

# 批量操作模式指定

operation_mode = request.query_params.get('mode', 'insert')

2.2 认证与授权信息

2.2.1 request.user(认证用户)

  • 功能:获取经过认证的用户实例,未认证时为AnonymousUser[^8][^18]
  • 企业级用法

def bulk_upload(self, request):

# 审计日志记录

logger.info(f"批量上传操作 - 用户: {request.user.username}, IP: {self.get_client_ip(request)}")

# 权限校验

if not request.user.has_perm('app.bulk_upload'):

return Response(

{"error": "无批量上传权限"},

status=status.HTTP_403_FORBIDDEN

)

2.2.2 request.auth(认证凭证)

  • 功能:获取认证凭证对象(如JWT令牌),用于高级权限控制[^8][^18]
  • 企业级用法

def bulk_upload(self, request):

# 令牌有效期检查

if request.auth and request.auth.exp < timezone.now():

return Response(

{"error": "令牌已过期"},

status=status.HTTP_401_UNAUTHORIZED

)

2.3 请求元数据与安全信息

2.3.1 request.META(请求头与环境变量)

  • 核心元数据

o HTTP_AUTHORIZATION: 认证头信息(如Bearer令牌)

o HTTP_X_FORWARDED_FOR: 客户端真实IP(支持代理场景)

o CONTENT_TYPE: 请求内容类型(如application/json)

o HTTP_USER_AGENT: 客户端设备信息[^8][^18]

  • 企业级用法

def get_client_ip(self, request):

"""安全获取客户端IP(支持代理服务器)"""

xff = request.META.get('HTTP_X_FORWARDED_FOR')

return xff.split(',')[0].strip() if xff else request.META.get('REMOTE_ADDR')

def bulk_upload(self, request):

# 防DDoS检查(限制请求体大小)

content_length = int(request.META.get('CONTENT_LENGTH', 0))

if content_length > 10 * 1024 * 1024: # 10MB限制

return Response(

{"error": "请求体超过最大限制"},

status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE

)

# GDPR合规性检查(强制JSON格式)

if request.content_type != 'application/json':

return Response(

{"error": "仅支持application/json格式"},

status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE

)

三、企业级批量上传实现案例

3.1 完整业务代码

import logging

from rest_framework import viewsets, status

from rest_framework.decorators import action

from rest_framework.response import Response

from django.utils import timezone

import uuid

logger = logging.getLogger(__name__)

class ResourceViewSet(viewsets.ModelViewSet):

queryset = Resource.objects.all()

serializer_class = ResourceSerializer

permission_classes = [IsAuthenticated] # 继承视图集的权限控制

@action(detail=False, methods=['post'], url_path='batch')

def bulk_upload(self, request):

"""

企业级批量上传实现

- 支持JSON/表单数据输入

- 包含完整的认证、授权、审计流程

- 实现数据验证与错误处理

"""

# 1. 请求审计(企业级合规要求)

request_id = str(uuid.uuid4())

client_ip = self.get_client_ip(request)

logger.info(

f"[批量上传] request_id={request_id}, user={request.user.username}, "

f"ip={client_ip}, content_type={request.content_type}"

)

# 2. 安全检查

if not self._validate_request(request):

return Response(

{"error": "请求验证失败", "request_id": request_id},

status=status.HTTP_400_BAD_REQUEST

)

# 3. 数据解析与验证

serializer = BulkResourceSerializer(data=request.data)

if not serializer.is_valid():

logger.warning(

f"[批量上传] 数据验证失败 request_id={request_id}, errors={serializer.errors}"

)

return Response(

{"error": "数据格式错误", "details": serializer.errors, "request_id": request_id},

status=status.HTTP_400_BAD_REQUEST

)

# 4. 批量业务处理

try:

result = self._process_batch(serializer.validated_data)

logger.info(

f"[批量上传] 处理成功 request_id={request_id}, "

f"created={result['created']}, updated={result['updated']}"

)

return Response({

"status": "success",

"data": result,

"request_id": request_id

}, status=status.HTTP_201_CREATED)

except Exception as e:

logger.error(

f"[批量上传] 处理失败 request_id={request_id}, error={str(e)}",

exc_info=True

)

return Response(

{"error": "服务器内部错误", "request_id": request_id},

status=status.HTTP_500_INTERNAL_SERVER_ERROR

)

def _validate_request(self, request):

"""请求安全验证"""

# 内容类型检查

if request.content_type not in ['application/json', 'application/x-www-form-urlencoded']:

return False

# 请求体大小检查

content_length = int(request.META.get('CONTENT_LENGTH', 0))

if content_length > 10 * 1024 * 1024: # 10MB

return False

return True

def _process_batch(self, validated_data):

"""批量数据处理逻辑"""

resources = validated_data['resources']

created_count = 0

updated_count = 0

# 使用bulk_create优化性能

create_list = []

for item in resources:

resource = Resource(**item, created_by=request.user)

create_list.append(resource)

if create_list:

Resource.objects.bulk_create(create_list)

created_count = len(create_list)

return {"created": created_count, "updated": updated_count}

3.2 关键技术点解析

1.** 性能优化:使用bulk_create批量创建数据,比循环创建效率提升10-100倍[^8]

2.安全防护 **:

  • 限制请求体大小防止DDoS攻击
  • 验证内容类型确保数据格式安全
  • 记录详细审计日志支持问题追溯

3.** 错误处理 **:

  • 使用UUID生成唯一请求ID便于故障排查
  • 区分客户端错误与服务器错误
  • 详细记录错误堆栈但返回友好提示

4.** 权限控制 **:

  • 继承视图集的permission_classes
  • 额外检查对象级权限
  • 基于用户角色限制操作范围

四、request对象高级特性

4.1 数据解析机制

DRF自动根据Content-Type选择解析器:

  • application/json→JSONParser
  • application/x-www-form-urlencoded→FormParser
  • multipart/form-data→MultiPartParser[^10][^19]

企业级配置示例:

# 局部配置解析器

class ResourceViewSet(viewsets.ModelViewSet):

parser_classes = [JSONParser, MultiPartParser] # 仅支持JSON和文件上传

4.2 原生Django请求访问

DRF的Request对象通过代理模式封装Django原生HttpRequest:

# 访问Django原生属性

raw_post_data = request._request.body # 原始请求体

session_data = request._request.session # Session数据

cookies = request._request.COOKIES # Cookie数据

# 通过__getattr__自动代理

method = request.method # 等同于request._request.method

path = request.path # 等同于request._request.path

** 注意 **:优先使用DRF提供的属性(如data、query_params),仅在必要时访问原生请求[^1][^14]

4.3 内容协商与响应格式

request对象支持内容协商,根据Accept头自动选择响应格式:

def bulk_upload(self, request):

# 获取客户端期望的响应格式

accepted_format = request.accepted_renderer.format

if accepted_format == 'csv':

return self._render_csv_response(data)

return Response(data)

五、最佳实践与常见问题

5.1 命名规范

  • URL路径:使用名词复数形式,如/resources/batch/
  • 方法命名:使用动词开头的蛇形命名,如bulk_upload
  • 权限控制:显式指定permission_classes,如[IsAdminUser][^4][^7]

5.2 常见错误处理

1.** 数据验证失败 **:

if not serializer.is_valid():

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

2.** 权限不足 **:

if not request.user.is_staff:

return Response(

{"error": "管理员专属功能"},

status=status.HTTP_403_FORBIDDEN

)

3.** 请求方法不允许 **:

@action(detail=False, methods=['post']) # 仅允许POST

def bulk_upload(self, request):

pass # 非POST请求自动返回405 Method Not Allowed

5.3 性能优化建议

1.** 批量操作:使用bulk_create/bulk_update减少数据库交互

2.分页处理:对大型结果集使用request.query_params实现分页

3.异步处理 **:对耗时操作使用Celery异步执行:

from celery import shared_task

@shared_task

def process_large_batch(data, user_id):

# 耗时处理逻辑

def bulk_upload(self, request):

# 立即返回响应

process_large_batch.delay(request.data, request.user.id)

return Response({"status": "任务已提交"}, status=status.HTTP_202_ACCEPTED)

通过以上内容,可全面掌握DRF视图集自定义方法中@action装饰器的配置与request对象的企业级应用,实现安全、高效、可维护的批量操作接口。

 

posted on 2025-08-26 10:35  GoGrid  阅读(31)  评论(0)    收藏  举报

导航