zoukankan      html  css  js  c++  java
  • 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提供了一个更好的方式。

  • 相关阅读:
    LeetCode 623. Add One Row to Tree
    LeetCode 894. All Possible Full Binary Trees
    LeetCode 988. Smallest String Starting From Leaf
    LeetCode 979. Distribute Coins in Binary Tree
    LeetCode 814. Binary Tree Pruning
    LeetCode 951. Flip Equivalent Binary Trees
    LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
    LeetCode 889. Construct Binary Tree from Preorder and Postorder Traversal
    LeetCode 687. Longest Univalue Path
    LeetCode 428. Serialize and Deserialize N-ary Tree
  • 原文地址:https://www.cnblogs.com/welan/p/10141126.html
Copyright © 2011-2022 走看看