博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
译-Django restfull framework 中API版本的管理
阅读量:6452 次
发布时间:2019-06-23

本文共 6506 字,大约阅读时间需要 21 分钟。

原文:

什么情况下会有多版本的 api 的需求

我们在升级服务的时候,通常是向后兼容的。这样我们在升级客户端代码的时候,便不会遇到太大的困难。然而,当移动端的api升级后,客户手机中的app客户端有可能不会升级,所以我们必须保证所有版本的API的正常运行。

一个系统应该有一个好的api版本控制:新的功能和更改应该在新的版本中。旧的客户端可以使用旧的API,新的客户端可以使用新版本的API。

DRF 中支持的版本管理方案

DRF中支持多种版本管理方案。

AcceptHeaderVersioning

通过接受请求标头传递版本号:

GET /bookings/ HTTP/1.1Host: example.comAccept: application/json; version=1.0复制代码

URLPathVersioning

将版本以变量的方式添加到url地址(通过VERSION_PARAM参数在DRF中指定路径):

urlpatterns = [    url(        r'^(?P
(v1|v2))/bookings/$', bookings_list, name='bookings-list' )]复制代码

NamespaceVersioning

通过 url namespace 来区分版本:

# urls.pyurlpatterns = [    url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),    url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))]复制代码

HostNameVersioning

通过域名来设置版本:

http://v1.example.com/bookings/http://v2.example.com/bookings/复制代码

QueryParameterVersioning

通过 get query string 参数来专递版本:

http://example.com/bookings/?version=0.1http://example.com/bookings/?version=0.2复制代码

DRF 中版本化代码

在DRF 文档中介绍了第一个版本控制的方法。如下:

创建 Serializer 和 ViewSet

class AccountSerializer(serializers.ModelSerializer):    class Meta:        model = Account        fields = ('id', 'account_name', 'users', 'created')class AccountViewSet(viewsets.ModelViewSet):    queryset = Account.objects.all()    serializer_class = AccountSerializer复制代码

如果我们需要更改/删除/添加一个字段,我们创建一个新的序列化程序并更改其中的字段。

class AccountSerializerVersion1(serializers.ModelSerializer):    class Meta:        model = Account        fields = ('id', 'account_name', 'users', 'created', 'updated')复制代码

然后我们在AccountViewSet中重新定义get_serializer_class方法:

def get_serializer_class(self):    if self.request.version == 'v1':        return AccountSerializerVersion1    return AccountSerializer复制代码

这是在ViewSet中重新定义序列化程序,权限类和其他方法的一种方法。

同时,我发现一个小的版本控制的。

我没有使用它,但是从文档中我们可以设置Serializer和Parser并使用它们来设置变换的基类。

from rest_framework_transforms.transforms import BaseTransformclass TestModelTransform0002(BaseTransform):    """    Changes between v1 and v2    """    def forwards(self, data, request):        if 'test_field_one' in data:            data['new_test_field'] = data.get('test_field_one')            data.pop('test_field_one')        return data    def backwards(self, data, request, instance):        data['test_field_one'] = data.get('new_test_field')        data.pop('new_test_field')        return data复制代码

设置基本版本:

class TestSerializerV3(BaseVersioningSerializer):    transform_base = 'tests.test_transforms.TestModelTransform'    class Meta:        model = TestModelV3        fields = (            'test_field_two',            'test_field_three',            'test_field_four',            'test_field_five',            'new_test_field',            'new_related_object_id_list',        )复制代码

我们这样创建每个新版本:

class TestModelTransform0003(BaseTransform):    """    Changes between v2 and v3    """    def forwards(self, data, request):        data['new_related_object_id_list'] = [1, 2, 3, 4, 5]        return data    def backwards(self, data, request, instance):        data.pop('new_related_object_id_list')        return data复制代码

从客户端接收数据(即0004,0003,0002)时,向后的方法将从结尾开始应用。向客户端发送数据时,转发将按照0002,0003,0004的顺序进行。

我们是如何处理版本的

基本思想是将API分解为模块并使用类继承。

如下目录结构:

api/├── base│   ├── init.py│   ├── router.py│   ├── serializers.py│   └── views.py├── init.py└── versioned    ├── init.py    ├── v2    │   ├── init.py    │   ├── router.py    │   ├── serializers.py    │   └── views.py    ├── v3    │   ├── init.py    │   ├── router.py    │   ├── serializers.py    │   └── views.py    ├── v4    │   ├── init.py    │   ├── router.py    │   ├── serializers.py    │   └── views.py    └── v5        ├── init.py        ├── router.py        ├── serializers.py        └── views.py复制代码

base - 我们的基础版本API,第一个版本。

此外,在版本化文件夹中,我们为每个版本创建了一个文件夹。在这个项目中,我们有两个外部客户:iOS和Android +我们的WEB客户端。 WEB客户端一直使用最新版本的API。

每个连续的API版本是这样处理的:我们在现有的API v2中进行了更改; 在iOS和Android客户端发布之后(他们同时发布),我们创建了v3,并停止对v2进行更改。

DRF使用类来创建ViewSet,Serializer,Permission。我们使用API​​版本之间的继承来完全复制ViewSets和Serializer。

# base/serializers.pyclass UserSerializer(serializers.ModelSerializer):    class Meta:        model = User        fields = ('id', 'first_name', 'last_name', 'email')class BookSerializer(serializers.ModelSerializer):    class Meta:        model = Book        fields = 'all'复制代码
# base/views.pyfrom . import serializersclass UserViewSet(viewsets.ModelViewSet):    queryset = User.objects.all()    serializer_class = serializers.UserSerializerclass BookViewSet(viewsets.ModelViewSet):    queryset = Book.objects.all()    serializer_class = serializers.BookSerializer复制代码
# base/router.pyfrom . import viewsrouter = routers.DefaultRouter()router.register(r'users', views.UserViewSet)router.register(r'books', views.BookViewSet)api_urlpatterns = router.urls复制代码

此外,我们将urls.py连接到第一个API版本:

from .api.base.router import api_urlpatterns as api_v1urlpatterns = [    url(r'^api/v1/', include(api_v1)),]复制代码

我们删除了first_name,last_name字段并添加了full_name字段。然后我们创建了v2保持向后兼容性,并添加了serializers.py,views.py,router.py目录和文件:

└── versioned    ├── init.py    ├── v2    │   ├── init.py    │   ├── router.py    │   ├── serializers.py    │   └── views.py复制代码

继承base 版本:

# versioned/v2/serializers.py# import all our basic serializersfrom .api.base import serializers as base_serializersfrom .api.base.serializers import *class UserSerializer(base_serializers.UserSerializer):    full_name = serializers.SerializerMethodField()    class Meta(base_serializers.UserSerializer.Meta):        fields = ('id', 'email', 'full_name')    def get_full_name(self, obj):        return '{0} {1}'.format(obj.first_name, obj.last_name)复制代码
# versioned/v2/views.pyfrom .api.base.views import *from .api.base import views as base_viewsfrom . import serializers as v2_serializersclass UserViewSet(base_views.UserViewSet):    serializer_class = v2_serializers.UserSerializer复制代码
# versioned/v2/router.pyfrom . import viewsrouter = routers.DefaultRouter()router.register(r'users', views.UserViewSet)router.register(r'books', views.BookViewSet)api_urlpatterns = router.urls复制代码

更新root url 文件:

from .api.base.router import api_urlpatterns as api_v1from .api.versioned.v2.router import api_urlpatterns as api_v2urlpatterns = [    url(r'^api/v1/', include(api_v1)),    url(r'^api/v2/', include(api_v2)),]复制代码

您可能会注意到我们已经继承了UserViewSet,而且我们没有更新BookViewSet,这是因为我们在v2视图视图中引入了base 的视图。

以上方法的优缺点

优点

  • 开发简单
  • 相关类的版本由模块基础,v1,v2等分类。
  • 易于浏览代码
  • 无需复制 view 和 serializers 的源代码。
  • 少 if 嵌套

缺点

  • 当API 版本过多时,会造成代码继承层数多大,不利于维护。
  • 应为要继承,需要简单修改部分代码。

总结

管理API版本可能相当困难,尤其是要正确实施。您可以在每个版本控制方法中找到优缺点。由于我们项目中的版本少,所以继承方法是比较实用的。

参考:

转载地址:http://tagwo.baihongyu.com/

你可能感兴趣的文章
贪吃蛇java程序简化版_JAVA简版贪吃蛇
查看>>
poi java web_WebPOI JavaWeb 项目 导出excel表格(.xls) Develop 238万源代码下载- www.pudn.com...
查看>>
java 顶点着色_金属顶点着色器绘制纹理点
查看>>
php扩展有哪些G11,php 几个扩展(extension)的安装笔记
查看>>
ajax长连接 php,ajax怎么实现服务器与浏览器长连接
查看>>
oracle报1405,【案例】Oracle报错ORA-15054 asm diskgroup无法mount的解决办法
查看>>
php 5.4.24 win32,PHP 5.4.14 和 PHP 5.3.24 发布
查看>>
oracle top pid,Linux Top 命令解析 比较详细
查看>>
grub如何进入linux系统,Linux操作系统启动管理器-GRUB
查看>>
linux pbs 用户时间,【Linux】单计算机安装PBS系统(Torque)与运维
查看>>
linux系统可用内存减少,在Linux中检查可用内存的5种方法
查看>>
linux 脚本map,Linux Shell Map的用法详解
查看>>
如何在linux系统下配置共享文件夹,如何在windows和Linux系统之间共享文件夹.doc
查看>>
thinkpad装linux无线网卡驱动,ThinkPad E530 Fedora 20 下无线网卡驱动的安装
查看>>
linux操作系统加固软件,系统安全:教你Linux操作系统的安全加固
查看>>
linux中yum源安装dhcp,24.Linux系统下动态网络源部署方法(dhcpd)
查看>>
linux屏幕复制显示出来的,linux – stdout到gnu屏幕复制缓冲区
查看>>
一起学Shell(十)之可称植性议题与扩展
查看>>
部署Ganglia监控Hadoop&Hbase
查看>>
gitlab的用户使用手册
查看>>