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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 02:10:39