RESTful 规范
一, 前言
1, 版本的重要性
在 RESTful 规范中, 有关版本的问题, 用 restful 规范做开放接口的时候, 用户请求 API, 系统返回数据. 但是难免在系统发展的过程中, 不可避免的需要添加新的资源, 或者修改现有资源. 因此, 改动升级必不可少, 但是, 作为平台开发者, 应该知道: 一旦你的 API 开放出去, 有人开始用了, 平台的任何改动都需要考虑对当前用户的影响. 因此, 做开放平台, 从第一个 API 的设计就需要开始 API 的版本控制策略问题, API 的版本控制策略就像是开放平台和平台用户之间的长期协议, 其设计的好坏将直接决定用户是否使用该平台, 或者说用户在使用之后是否会因为某次版本升级直接弃用该平台.
2, 定义版本
怎么定义版本协议, 前端后端怎么协调. 有以下几种方式:
请求头中定义
- GET /something/ HTTP/1.1
- Host: example.com
- Accept: application/JSON; version=1.0 #版本为 1.0
URL 中定义
- URL: example.com/v1.0/ # 版本为 1.0
- GET /1.0/something/ HTTP/1.1
- Host: example.com
- Accept: application/JSON
子域名中定义
- GET /something/ HTTP/1.1
- Host: v1.example.com # 版本为 1.0
- Accept: application/JSON
HttpReqeust 参数传递
- GET /something/?version=0.1 HTTP/1.1 # 版本为 1.0
- Host: example.com
- Accept: application/JSON
二, 示例
在 django REST framewrok 中, 如果没有在配置文件 setting.py 中设置默认的 VERSION_PARAM, 即版本参数, drf 会设置默认的参数为 version, 并将获取到的 version 的值封装到 request.version 中
1, 请求头中定义
django REST framework 的 request 其实是对原生的 Django 的 HttpRequest 做了一个封装, 通过直接获取属性可以获取到请求头中的版本号
django REST framework 的 request
原生的 Django 的 HttpRequest
请求头的版本和其他请求头信息最终会放到 META 中, 因此想要获取版本号可以如下这样
version = request._request.META.get('version') # 获取版本号
2, 子域名中定义
同样的像请求头中定义一样, 在请求头中也可以直接获取的域名, 放到 META 中, 因此想要获取版本号可以如下这样
- host = request._request.META.get('HTTP_HOST') # 先获取主机域名
- version = host.split('.')[0] # 获取版本号
注: 其实在 django REST framework 内部也有关于以上两种定义版本的处理方法
3,HttpReqeust 参数传递
之前分别在 django REST framework 中关于节流, 认证, 权限三个组件, 这里新建一个 Django 项目, 命名为 drf2. 并进入当前目录下执行 python manage.py startapp API, 将新建的 App, 和 rest_framework 放入 INSTALLED_APPS.
- # setting.py
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'rest_framework',
- 'api'
- ]
- <1>, 目录结构
- <2>, 路由系统
- from django.conf.urls import url
- from .views import VersionView
- urlpatterns = [
- url(r'^version/$', VersionView.as_view()),
- ]
- <3>, 视图
- from django.http import JsonResponse
- from rest_framework.views import APIView
- from rest_framework.versioning import QueryParameterVersioning
- class VersionView(APIView):
- versioning_class = QueryParameterVersioning # 局部配置请求参数处理
- def get(self, request, *args, **kwargs):
- version = request.version
- ret = {
- 'code': 1000,
- 'msg': '请求成功',
- 'version': version
- }
- return JsonResponse(ret)
- <4>, 配置文件
像之前在权限, 节流那样, 可以配置一个全局默认的版本解析类
- REST_FRAMEWORK = {
- "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", # 默认是 url 处理版本
- "DEFAULT_VERSION":'v1', # 默认版本
- "ALLOWED_VERSIONS":['v1','v2'], # 允许版本
- "VERSION_PARAM":'version', # 版本参数例如 ?version=v1,, 则表示版本为 v1
- }
- <5>, 测试
使用 postman 或者浏览器发送请求测试
提供正常版本号: http://127.0.0.1:8000/api/version/?version=v1 获取版本成功
发送错误版本号: http://127.0.0.1:8000/api/version/?version=v3 由于允许版本只有 v1 和 v2, 所以版本错误, 返回错误信息
不提供版本号: 假如在 url 请求中不添加参数, http://127.0.0.1:8000/api/version/?, 能获取到默认的版本号
4,URL 中定义
在 url 中定义, 例如 http://127.0.0.1:8000/api/v1/
- <1>, 路由系统
- from django.conf.urls import url
- from .views import VersionView
- urlpatterns = [
- url(r'^(?P<version>[v1|v2]+)/$', VersionView.as_view()), # 可用版本为 v1 和 v2
- ]
- <2>, 视图
- from django.http import JsonResponse
- from django.http import HttpRequest
- from rest_framework.views import APIView, Request
- from rest_framework.versioning import URLPathVersioning
- class VersionView(APIView):
- versioning_class = URLPathVersioning # 局部配置版本类
- def get(self, request, *args, **kwargs):
- version = request.version
- ret = {
- 'code': 1000,
- 'msg': '请求成功',
- 'version': version
- }
- return JsonResponse(ret)
或者也可以全局配置, 不过使用 URL 解析的时候, 需要在路由系统中正则匹配设置可用的版本,
- REST_FRAMEWORK = {
- "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
- "VERSION_PARAM":'version', # 参数
- }
- <3>, 测试
使用 postman 或者浏览器发送请求测试
http://127.0.0.1:8000/api/v1/, 正确的获取版本号
5, 反向解析 URL
在 django 中也提供了一个 url 解析的函数 reverse, 不过在 django REST framework 中也有一个将 reverse 函数封装一层的接口可以进行 url 反向解析.
路由系统: 加入 namespace 参数
- from django.contrib import admin
- from django.urls import path, include
- from django.conf.urls import url
- urlpatterns = [
- url(r'^api/', include('api.urls', namespace='api') ),
- ]
子路由系统: 加入 name 参数
- from django.conf.urls import url
- from .views import VersionView
- app_name = 'api'
- urlpatterns = [
- url(r'^version/$', VersionView.as_view(), name='version'),
- url(r'^(?P<version>[v1|v2]+)/$', VersionView.as_view(), name='version'),
- ]
示例一: 参数携带版本
http://127.0.0.1:8000/api/version/?version=v1, 发送请求
- class VersionView(APIView):
- versioning_class = QueryParameterVersioning
- def get(self, request, *args, **kwargs):
- version = request.version
- url1 = request.versioning_scheme.reverse(viewname='api:version', request=request)
- url2 = reverse(viewname='api:version', kwargs=kwargs)
- ret = {
- 'code': 1000,
- 'msg': '请求成功',
- 'version': version,
- 'drf_url': url1,
- 'django_url': url2
- }
- return JsonResponse(ret)
使用 postman 返送请求
示例二: URL 携带版本
http://127.0.0.1:8000/api/v1/, 发送请求
- class VersionView(APIView):
- versioning_class = URLPathVersioning
- def get(self, request, *args, **kwargs):
- version = request.version
- url1 = request.versioning_scheme.reverse(viewname='api:version', request=request)
- url2 = reverse(viewname='api:version', kwargs=kwargs)
- ret = {
- 'code': 1000,
- 'msg': '请求成功',
- 'version': version,
- 'drf_url': url1,
- 'django_url': url2
- }
- return JsonResponse(ret)
使用 postman 返送请求
这里有与 drf 的 reverse 在对 django 中的 reverse 函数进行封装的时候, 获取了 request.get_full_url(), 并做了一个拼接, 所以才会出现全部的 url
三, 源码分析
1, 找到 initial() 方法
依旧从 dispath 方法进入源码, 找到 initial 方法
2, 进入 initial() 方法
这里调用了 determine_version() 方法, 并拿到两个返回值并封装到 request 中. 这时候 request.version_scheme 就是一个版本对象了
3, 查看具体的 determine_version() 方法
4, 默认的版本处理对象
可以在 setting.py 中配置之后, 全局使用
5,drf 提供的版本类
在 url 反向解析中, 调用了 request.versioning_scheme.reverse() 中的 reverse() 方法, 说明 request.versioning_scheme 返回的是一个版本对象, 可以调用他的方法
BaseVersioning 基类定义了三个接口
determine_version: 返回版本
reverse:url 反向解析使用
is_allowed_version: 就是判断版本号是否合法
而上面示例使用的两个超类 URLPathVersioning,QueryParameterVersioning 其实也就是, 重写了 determine_version, 和 reverse 两个方法.
四, 总结
版本的获取方式有多种, 在 django REST framewok 中也提供了一一对应的处理版本对象, 可以根据自己的需要配置, 或者继承重写接口使用.
配置也支持全局配置, 和局部配置, 在全局配置的时候, 需要定义默认的版本号, 以防万一.
在进行 url 反向解析的时候 django REST framewok 提供了一个更好的方式.
来源: https://www.cnblogs.com/welan/p/10141126.html