• Django Rest framework 之 版本


    一、前言

    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 frameworkrequest其实是对原生的DjangoHttpRequest做了一个封装,通过直接获取属性可以获取到请求头中的版本号
    django rest framework的request

    原生的DjangoHttpRequest

    请求头的版本和其他请求头信息最终会放到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提供了一个更好的方式。

  • 相关阅读:
    centos 安装mysql
    mysql中文排序
    在Centos中yum安装和卸载软件的使用方法
    gcc升级方法
    tar命令
    wget命令
    php7安装及和php5的共存
    PHP SESSION 保存到数据库
    setTimeout延时0毫秒的作用和问题
    css display visibility
  • 原文地址:https://www.cnblogs.com/welan/p/10141126.html
Copyright © 2020-2023  润新知