django-rest-framework: api 版本控制

     2023-02-24     187

关键词:

【中文标题】django-rest-framework: api 版本控制【英文标题】:django-rest-framework: api versioning 【发布时间】:2012-12-25 12:52:55 【问题描述】:

谷歌搜索似乎普遍认为,在 REST URI 中嵌入版本号是一种不好的做法和一个坏主意。

即使在 SO 上,也有强烈的支持者支持这一点。 例如Best practices for API versioning?

我的问题是关于如何完成建议的解决方案,即在 django-rest-framework 中使用接受标头/内容协商来完成此任务。

看起来像是框架中的内容协商,http://django-rest-framework.org/api-guide/content-negotiation/ 已配置为根据接受的 MIME 类型自动返回预期值。如果我开始对自定义类型使用 Accept 标头,我将失去框架的这种好处。

在框架中是否有更好的方法来实现这一点?

【问题讨论】:

【参考方案1】:

@James Lin 给出了很好的答案。在 cmets 中回答 @Mar0ux 询问如何处理损坏的 HyperlinkedRelatedField 字段。

我通过将HyperlinkedRelatedField 更改为SerializerMethodField 并调用reverse 解决了这个问题,非常不明显,将额外的参数current_app 传递给它。

例如,我有一个应用程序“fruits_app”,命名空间版本为“v1”、“v2”。我有水果模型的序列化器。所以要序列化 ​​url 我创建了一个字段

url = serializers.SerializerMethodField()

以及对应的方法:

def get_url(self, instance):
    reverse.reverse('fruits_app:fruit-detail',
        args=[instance.pk],
        request=request,
        current_app=request.version)

使用嵌套命名空间,您需要将这些命名空间添加到 current_app。例如,如果您有一个应用程序“fruits_app”,其名称空间版本为“v1”、“v2”和实例名称空间“bananas”,则序列化 Fruit url 的方法如下所示:

def get_url(self, instance):
    reverse.reverse('fruits_app:fruit-detail',
        args=[instance.pk],
        request=request,
        current_app='bananas:'.format(request.version))

【讨论】:

【参考方案2】:

更新:

versioning 现已得到适当支持。


您的链接中有一些答案:

我们发现将版本放在 URL 中既实用又有用。它 让您一目了然地知道您正在使用什么。我们做别名 /foo 到 /foo/(最新版本) 以便于使用,更短/更清晰的 URL, 等等,正如公认的答案所暗示的那样。 永远保持向后兼容性通常成本高昂和/或非常困难。我们更愿意提前通知 弃用、重定向,如建议的此处、文档和其他 机制。

所以我们采用了这种方法,并允许客户端在请求标头中指定版本(X-Version),我们是这样做的:

API 应用程序内部的结构:

.
├── __init__.py
├── middlewares.py
├── urls.py
├── v1
│   ├── __init__.py
│   ├── account
│   │   ├── __init__.py
│   │   ├── serializers.py
│   │   └── views.py
│   └── urls.py
└── v2
    ├── __init__.py
    ├── account
    │   ├── __init__.py
    │   ├── serializers.py
    │   └── views.py
    └── urls.py

项目 urls.py:

url(r'^api/', include('project.api.urls', namespace='api')),

api 应用级 urls.py:

from django.conf.urls import *

urlpatterns = patterns('',
    url(r'', include('project.api.v2.urls', namespace='default')),
    url(r'^v1/', include('project.api.v1.urls', namespace='v1')),
)

版本级 urls.py

from django.conf.urls import *
from .account import views as account_views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('account', account_views.AccountView)
router.register('myaccount', account_views.MyAccountView)
urlpatterns = router.urls

创建一个中间件,通过更改 path_info 来切换到正确的代码,请注意项目级 url 中定义的命名空间 ('api') 不灵活,需要在中间件中知道:

from django.core.urlresolvers import resolve
from django.core.urlresolvers import reverse


class VersionSwitch(object):

    def process_request(self, request):
        r = resolve(request.path_info)
        version = request.META.get('HTTP_X_VERSION', False)
        if r.namespace.startswith('api:') and version:
            old_version = r.namespace.split(':')[-1]
            request.path_info = reverse(':'.format(r.namespace.replace(old_version, version), r.url_name), args=r.args, kwargs=r.kwargs)

示例网址:

curl -H "X-Version: v1" http://your.domain:8000/api/myaccount/

【讨论】:

这种方法很好,除了它会破坏超链接字段(HyperlinkedRelatedField 等)。有什么想法吗? 我还没有项目设置可以使用HyperlinkedRelatedField 我猜你的问题是如果你指定了不同的版本,链接会转到默认版本? 没错。我倾向于使用 Accept 标头版本控制方法,这样 URL 根本不会改变。 恐怕没有简单的方法可以解决,直到版本控制被烘焙到包中,所以它会生成HyperlinkedRelatedField的版本感知网址 那么我们把模型放在哪里呢?【参考方案3】:

这样做的一种方法是将版本控制指定为媒体类型的一部分。

这是 GitHub currently do for their API。

您还可以在接受标头中包含媒体类型参数,例如Accept: application/json; version=beta,它将成功匹配JSONRenderer。然后,您可以根据接受的媒体类型对视图进行编码,使其行为不同,请参阅here。

API 中的版本控制有很多不同的模式,我不会说围绕正确的方法有任何很好的共识,但这是一种合理的可能性。


2015 年 1 月更新:3.1.0 版本中将提供更好的版本控制支持。请参阅 [此拉取请求]

2015 年 3 月更新:版本控制 API are now available 的文档。

(https://github.com/tomchristie/django-rest-framework/pull/2285) 了解更多详情。

【讨论】:

显然这个问题已经赢得了一个受欢迎的问题徽章,只是意识到我从未接受过答案。感谢您在 Tom 框架上的所有辛勤工作!

记录对 django-rest-framework 的请求

】记录对django-rest-framework的请求【英文标题】:Loggingrequeststodjango-rest-framework【发布时间】:2013-03-1219:37:12【问题描述】:出于调试目的,我想使用Django的日志记录机制来记录每个“到达”django-rest-framework门口的传入请求。Djagno... 查看详情

django-rest-framework:如何序列化已经包含 JSON 的字段?

】django-rest-framework:如何序列化已经包含JSON的字段?【英文标题】:django-rest-framework:HowDoISerializeaFieldThatAlreadyContainsJSON?【发布时间】:2014-04-0501:41:03【问题描述】:我对django-rest-framework还是很陌生,所以需要一些帮助。我有一... 查看详情

django-rest-framework: api 版本控制

】django-rest-framework:api版本控制【英文标题】:django-rest-framework:apiversioning【发布时间】:2012-12-2512:52:55【问题描述】:谷歌搜索似乎普遍认为,在RESTURI中嵌入版本号是一种不好的做法和一个坏主意。即使在SO上,也有强烈的支持... 查看详情

django-rest-framework 按日期过滤=无

】django-rest-framework按日期过滤=无【英文标题】:django-rest-frameworkfilterbydate=None【发布时间】:2014-02-2611:35:00【问题描述】:我正在使用django-rest-framework和django-filter。我需要检索date属性为None的小部件列表,但无论我尝试什么查询... 查看详情

django-rest-framework、多表模型继承、ModelSerializers 和嵌套序列化器

】django-rest-framework、多表模型继承、ModelSerializers和嵌套序列化器【英文标题】:django-rest-framework,multitablemodelinheritance,ModelSerializersandnestedserializers【发布时间】:2014-07-2518:17:22【问题描述】:我在文档或互联网上找不到此信息。... 查看详情

为啥 django-rest-framework 不显示 OneToOneField 数据 - django

】为啥django-rest-framework不显示OneToOneField数据-django【英文标题】:Whydjango-rest-frameworkdoesn\'tdisplayOneToOneFielddata-django为什么django-rest-framework不显示OneToOneField数据-django【发布时间】:2018-02-1616:26:13【问题描述】:我想在我的网站中... 查看详情

django-rest-framework:每个 ViewSet 可以有多个模板吗?

】django-rest-framework:每个ViewSet可以有多个模板吗?【英文标题】:django-rest-framework:CanIhavemultipletemplatesperViewSet?【发布时间】:2020-07-0701:30:04【问题描述】:我为我的模型创建了一个序列化程序和ViewSet,并为列表视图添加了一个... 查看详情

我无法使用 Django-Rest-Framework 注册用户

】我无法使用Django-Rest-Framework注册用户【英文标题】:IcannotregisterauserusingDjango-Rest-Framework【发布时间】:2021-04-0621:11:54【问题描述】:我无法使用DjangoRestFramework添加新用户。这是我的models.py代码:classProfile(models.Model):user=models.O... 查看详情

使用 django-rest-framework 创建对象后返回 id 值

】使用django-rest-framework创建对象后返回id值【英文标题】:Returningidvalueafterobjectcreationwithdjango-rest-framework【发布时间】:2013-09-1920:32:03【问题描述】:我正在使用django-rest-framework通用视图通过POST请求在模型中创建对象。我想知道... 查看详情

在 django-rest-framework 中捕获参数

】在django-rest-framework中捕获参数【英文标题】:Captureparametersindjango-rest-framework【发布时间】:2014-02-1302:01:42【问题描述】:假设这个网址:http://localhost:8000/articles/1111/comments/我想获取给定文章的所有cmets(此处为1111)。这就是... 查看详情

django-rest-framework 是不是提供管理站点来管理模型?

】django-rest-framework是不是提供管理站点来管理模型?【英文标题】:Doesthedjango-rest-frameworkprovideanadminsitetomanagemodels?django-rest-framework是否提供管理站点来管理模型?【发布时间】:2020-12-1812:02:18【问题描述】:我正在寻找使用djang... 查看详情

Django-rest-framework + React-Admin:URL 反斜杠问题

】Django-rest-framework+React-Admin:URL反斜杠问题【英文标题】:Django-rest-framework+React-Admin:URLBackslashissue【发布时间】:2019-02-0317:59:22【问题描述】:我有一个djangorestapi,它实现了如下突出显示的视图集。classSubjectViewSet(viewsets.ModelView... 查看详情

无法使用视图名称 (django-rest-framework) 解析超链接关系的 URL

】无法使用视图名称(django-rest-framework)解析超链接关系的URL【英文标题】:CouldnotresolveURLforhyperlinkedrelationshipusingviewname(django-rest-framework)【发布时间】:2018-06-2919:50:26【问题描述】:问题:我收到这样的错误。在/api/users/处配置不... 查看详情

在视图集中创建的 Django-rest-framework 权限

】在视图集中创建的Django-rest-framework权限【英文标题】:Django-rest-frameworkpermissionsforcreateinviewset【发布时间】:2014-05-1017:03:02【问题描述】:我正在尝试创建一个RESTAPI并且卡在用户注册上:基本上我需要在注册之前获得访问令牌... 查看详情

django-rest-framework 令牌认证和注销

】django-rest-framework令牌认证和注销【英文标题】:django-rest-frameworkTokenAuthandlogout【发布时间】:2015-03-0707:21:07【问题描述】:据了解,obtain_auth_token视图用作登录功能。您提供凭据并取回令牌。什么会注销?我应该在注销时删除... 查看详情

Django-Rest-Framework 系统检查自定义 HTTP 标头(应用程序 - 令牌)

】Django-Rest-Framework系统检查自定义HTTP标头(应用程序-令牌)【英文标题】:Django-Rest-FrameworksystemtocheckcustomHTTPheader(application-token)【发布时间】:2020-04-1707:05:30【问题描述】:我使用Django和Django-rest-framework。我必须检查一个自定... 查看详情

如何在 django-rest-framework 中对权限进行单元测试?

】如何在django-rest-framework中对权限进行单元测试?【英文标题】:Howtounittestpermissionsindjango-rest-framework?【发布时间】:2016-09-0708:14:14【问题描述】:她的权限是我想要进行单元测试的示例权限。#permissions.pyfrommyapp.modelsimportMembersh... 查看详情

为啥使用 django-rest-framework 时不需要 `csrf_exempt`?

】为啥使用django-rest-framework时不需要`csrf_exempt`?【英文标题】:Whyis`csrf_exempt`notneededwhenusingdjango-rest-framework?为什么使用django-rest-framework时不需要`csrf_exempt`?【发布时间】:2022-01-1323:32:30【问题描述】:当我使用Postman发出POST请... 查看详情