如何在Django Rest Framework中优雅弃用API端点?
在DRF中优雅标记API端点为弃用的方案
问题背景
在Django Rest Framework(DRF)里,官方文档几乎找不到自定义端点弃用的实操说明,谷歌搜出来的大多是Django的变更日志,不是业务API里标记弃用的具体方法。我现在遵循API演进原则开发,要把一个不符合REST规范的端点标记为弃用/停用,而且API没做版本号区分。目前的做法是在响应里加Warning头,但觉得这不是DRF的标准玩法,想找更优的实现方式。
待弃用的端点代码:
# 这个不符合REST标准的旧action需要弃用 @action(url_path="filter", methods=["post"], detail=False) def get_filtered(self, request, *args, **kwargs): # 业务逻辑代码 return filtered_queryset
当前非标准实现:
@action(url_path="filter", methods=["post"], detail=False) def get_filtered(self, request, *args, **kwargs): response = filtered_queryset response['Warning'] = 'This endpoint is deprecated and will be removed on April 1st 2023' return response
优化方案
方案1:自定义弃用装饰器(DRF风格)
自己写一个通用装饰器,统一处理弃用逻辑,包括添加标准响应头、记录调用日志,符合DRF的扩展思路,还能复用在其他需要弃用的端点上。
装饰器代码:
from rest_framework.response import Response import logging logger = logging.getLogger(__name__) def deprecated(message, removal_date): def decorator(view_func): def wrapped_view(*args, **kwargs): # 调用原视图获取响应 response = view_func(*args, **kwargs) # 按照RFC 7234规范添加Warning头(299是自定义警告码) warning_value = f'299 - "{message} Will be removed on {removal_date}"' response['Warning'] = warning_value # 记录弃用请求的日志,方便统计哪些客户端还在调用 request = args[1] logger.warning(f"Deprecated endpoint accessed: {request.path} | User: {request.user}") return response return wrapped_view return decorator
使用方式:
@action(url_path="filter", methods=["post"], detail=False) @deprecated(message="This endpoint is deprecated", removal_date="April 1st 2023") def get_filtered(self, request, *args, **kwargs): # 业务逻辑代码 return filtered_queryset
方案2:结合状态码与重定向(有替代端点时用)
如果已经有符合REST规范的替代端点,直接用301永久重定向过去,同时加Warning头提示;如果端点已经彻底停用,返回410 Gone状态码明确告知客户端资源已移除。
重定向到新端点示例:
from rest_framework.response import Response from rest_framework import status from django.urls import reverse @action(url_path="filter", methods=["post"], detail=False) def get_filtered(self, request, *args, **kwargs): response = Response(status=status.HTTP_301_MOVED_PERMANENTLY) # 替换成你的新端点名称 response['Location'] = reverse('new-resource-filter', request=request) response['Warning'] = 'This endpoint is deprecated and will be removed on April 1st 2023. Use the new endpoint instead.' return response
彻底停用返回410示例:
from rest_framework.response import Response from rest_framework import status @action(url_path="filter", methods=["post"], detail=False) def get_filtered(self, request, *args, **kwargs): return Response( data={"detail": "该端点已弃用并移除,请使用新的/api/resources/filter端点"}, status=status.HTTP_410_GONE, headers={"Warning": '299 - "Deprecated endpoint removed on April 1st 2023"'} )
方案3:限流逐步停用(可选)
如果想平滑过渡,不想直接砍端点,可以给弃用的端点加限流,降低请求上限,同时配合头信息和日志,倒逼依赖方迁移到新端点。
示例代码:
from rest_framework.throttling import UserRateThrottle # 自定义限流类,限制弃用端点的请求次数 class DeprecatedEndpointThrottle(UserRateThrottle): rate = '10/day' # 每个用户每天最多调用10次 @action(url_path="filter", methods=["post"], detail=False, throttle_classes=[DeprecatedEndpointThrottle]) @deprecated(message="This endpoint is deprecated", removal_date="April 1st 2023") def get_filtered(self, request, *args, **kwargs): # 业务逻辑代码 return filtered_queryset
内容的提问来源于stack exchange,提问作者David Sigley
相关产品推荐
相关产品推荐

