专业3合1网站建设公司,网站建设和维护面试题,制作可以赚钱的网站,中国沙漠建设 志愿者 兵团官方网站API 版本控制允许我们在不同的客户端之间更改行为#xff08;同一个接口的不同版本会返回不同的数据#xff09;。 DRF提供了许多不同的版本控制方案。
可能会有一些客户端因为某些原因不再维护了#xff0c;但是我们后端的接口还要不断的更新迭代#xff0c;这个时候通过…API 版本控制允许我们在不同的客户端之间更改行为同一个接口的不同版本会返回不同的数据。 DRF提供了许多不同的版本控制方案。
可能会有一些客户端因为某些原因不再维护了但是我们后端的接口还要不断的更新迭代这个时候通过版本控制返回不同的内容就是一种不错的解决方案。
DRF提供的版本控制方案
DRF提供了五种版本控制方案如下图 版本控制系统的使用
全局配置
这里我们以 URLPathVersioning 为例还是在项目的settings.py中REST_FRAMEWORK配置项下配置
REST_FRAMEWORK {...DEFAULT_VERSIONING_CLASS: rest_framework.versioning.URLPathVersioning,DEFAULT_VERSION: v1, # 默认的版本ALLOWED_VERSIONS: [v1, v2], # 有效的版本VERSION_PARAM: version, # 版本的参数名与URL conf中一致
}
局部配置
注意通常我们是不会单独给某个视图设置版本控制的如果你确实需要给单独的视图设置版本控制你可以在视图中设置versioning_class属性如下
class PublisherViewSet(ModelViewSet):...versioning_class URLPathVersioning
urls.py
urlpatterns [...url(r^(?Pversion[v1|v2])/publishers/$, views.PublisherViewSet.as_view({get: list, post: create})),url(r^(?Pversion[v1|v2])/publishers/(?Ppk\d)/$, views.PublisherViewSet.as_view({get: retrieve, put: update, delete: destroy})),]
我们在视图中可以通过访问 request.version 来获取当前请求的具体版本然后根据不同的版本来返回不同的内容
我们可以在视图中自定义具体的行为下面以不同的版本返回不同的序列化类为例
class PublisherViewSet(ModelViewSet):def get_serializer_class(self):不同的版本使用不同的序列化类if self.request.version v1:return PublisherModelSerializerVersion1else:return PublisherModelSerializerqueryset models.Publisher.objects.all()
REST framework 提供的默认版本的源码
############################## versioning.py #############################
# coding: utf-8
from __future__ import unicode_literalsimport refrom django.utils.translation import ugettext_lazy as _from rest_framework import exceptions
from rest_framework.compat import unicode_http_header
from rest_framework.reverse import _reverse
from rest_framework.settings import api_settings
from rest_framework.templatetags.rest_framework import replace_query_param
from rest_framework.utils.mediatypes import _MediaType# 基础类其他类要继承此类
class BaseVersioning(object):default_version api_settings.DEFAULT_VERSION # 配置文件中获取相应信息allowed_versions api_settings.ALLOWED_VERSIONSversion_param api_settings.VERSION_PARAMdef determine_version(self, request, *args, **kwargs): # 必须实现的类msg {cls}.determine_version() must be implemented.raise NotImplementedError(msg.format(clsself.__class__.__name__))def reverse(self, viewname, argsNone, kwargsNone, requestNone, formatNone, **extra):return _reverse(viewname, args, kwargs, request, format, **extra)def is_allowed_version(self, version): # 检测版本是否允许if not self.allowed_versions:return Truereturn ((version is not None and version self.default_version) or(version in self.allowed_versions))# 版本信息在头部
class AcceptHeaderVersioning(BaseVersioning):GET /something/ HTTP/1.1Host: example.comAccept: application/json; version1.0invalid_version_message _(Invalid version in Accept header.)def determine_version(self, request, *args, **kwargs):media_type _MediaType(request.accepted_media_type)version media_type.params.get(self.version_param, self.default_version)version unicode_http_header(version)if not self.is_allowed_version(version):raise exceptions.NotAcceptable(self.invalid_version_message)return version# We dont need to implement reverse, as the versioning is based# on the Accept header, not on the request URL.# 版本信息在url中
class URLPathVersioning(BaseVersioning):To the client this is the same style as NamespaceVersioning.The difference is in the backend - this implementation usesDjangos URL keyword arguments to determine the version.An example URL conf for two views that accept two different versions.urlpatterns [url(r^(?Pversion[v1|v2])/users/$, users_list, nameusers-list),url(r^(?Pversion[v1|v2])/users/(?Ppk[0-9])/$, users_detail, nameusers-detail)]GET /1.0/something/ HTTP/1.1Host: example.comAccept: application/jsoninvalid_version_message _(Invalid version in URL path.)def determine_version(self, request, *args, **kwargs):version kwargs.get(self.version_param, self.default_version)if version is None:version self.default_versionif not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return versiondef reverse(self, viewname, argsNone, kwargsNone, requestNone, formatNone, **extra):if request.version is not None:kwargs {} if (kwargs is None) else kwargskwargs[self.version_param] request.versionreturn super(URLPathVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)class NamespaceVersioning(BaseVersioning):To the client this is the same style as URLPathVersioning.The difference is in the backend - this implementation usesDjangos URL namespaces to determine the version.An example URL conf that is namespaced into two separate versions# users/urls.pyurlpatterns [url(r^/users/$, users_list, nameusers-list),url(r^/users/(?Ppk[0-9])/$, users_detail, nameusers-detail)]# urls.pyurlpatterns [url(r^v1/, include(users.urls, namespacev1)),url(r^v2/, include(users.urls, namespacev2))]GET /1.0/something/ HTTP/1.1Host: example.comAccept: application/jsoninvalid_version_message _(Invalid version in URL path. Does not match any version namespace.)def determine_version(self, request, *args, **kwargs):resolver_match getattr(request, resolver_match, None)if resolver_match is None or not resolver_match.namespace:return self.default_version# Allow for possibly nested namespaces.possible_versions resolver_match.namespace.split(:)for version in possible_versions:if self.is_allowed_version(version):return versionraise exceptions.NotFound(self.invalid_version_message)def reverse(self, viewname, argsNone, kwargsNone, requestNone, formatNone, **extra):if request.version is not None:viewname self.get_versioned_viewname(viewname, request)return super(NamespaceVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)def get_versioned_viewname(self, viewname, request):return request.version : viewnameclass HostNameVersioning(BaseVersioning):GET /something/ HTTP/1.1Host: v1.example.comAccept: application/jsonhostname_regex re.compile(r^([a-zA-Z0-9])\.[a-zA-Z0-9]\.[a-zA-Z0-9]$)invalid_version_message _(Invalid version in hostname.)def determine_version(self, request, *args, **kwargs):hostname, separator, port request.get_host().partition(:)match self.hostname_regex.match(hostname)if not match:return self.default_versionversion match.group(1)if not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return version# We dont need to implement reverse, as the hostname will already be# preserved as part of the REST framework reverse implementation.# 通过url参数区分
class QueryParameterVersioning(BaseVersioning):GET /something/?version0.1 HTTP/1.1Host: example.comAccept: application/jsoninvalid_version_message _(Invalid version in query parameter.)def determine_version(self, request, *args, **kwargs):version request.query_params.get(self.version_param, self.default_version)if not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return versiondef reverse(self, viewname, argsNone, kwargsNone, requestNone, formatNone, **extra):url super(QueryParameterVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)if request.version is not None:return replace_query_param(url, self.version_param, request.version)return url