• RESTful API


    一. 什么是RESTful 

    • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
    • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
    • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
    • 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
    • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

    二. RESTful API设计

    • API与用户的通信协议,总是使用HTTPs协议
    • 域名 
      • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
      • https://example.org/api/                        API很简单
    • 版本
      • URL,如:https://api.example.com/v1/
      • 请求头                                                  跨域时,引发发送多次请求
    • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)
      • https://api.example.com/v1/zoos
      • https://api.example.com/v1/animals
      • https://api.example.com/v1/employees
    • method
      • GET      :从服务器取出资源(一项或多项)
      • POST    :在服务器新建一个资源
      • PUT      :在服务器更新资源(客户端提供改变后的完整资源)
      • PATCH  :在服务器更新资源(客户端提供改变的属性)
      • DELETE :从服务器删除资源
    • 过滤,通过在url上传参的形式传递搜索条件
      • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
      • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
      • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
      • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
      • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
    • 状态码
      200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
      201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
      202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
      204 NO CONTENT - [DELETE]:用户删除数据成功。
      400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
      401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
      403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
      404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
      406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
      410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
      422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
      500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
      
      更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
      常用状态码列表
    • 错误处理,状态码是4xx时,应返回错误信息,error当做key。
      1
      2
      3
      {
          error: "Invalid API key"
      }
    • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
      1
      2
      3
      4
      5
      6
      GET /collection:返回资源对象的列表(数组)
      GET /collection/resource:返回单个资源对象
      POST /collection:返回新生成的资源对象
      PUT /collection/resource:返回完整的资源对象
      PATCH /collection/resource:返回完整的资源对象
      DELETE /collection/resource:返回一个空文档
    • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
      1
      2
      3
      4
      5
      6
      {"link": {
        "rel":   "collection https://www.example.com/zoos",
        "href""https://api.example.com/zoos",
        "title": "List of zoos",
        "type""application/vnd.yourformat+json"
      }}

      摘自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html 

    三. 基于Django实现

    路由系统:

    1
    2
    3
    urlpatterns = [
        url(r'^users', Users.as_view()),
    ]

    CBV视图:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from django.views import View
    from django.http import JsonResponse
     
    class Users(View):
        def get(self, request, *args, **kwargs):
            result = {
                'status': True,
                'data': 'response data'
            }
            return JsonResponse(result, status=200)
     
        def post(self, request, *args, **kwargs):
            result = {
                'status': True,
                'data': 'response data'
            }
            return JsonResponse(result, status=200

    四. 基于Django Rest Framework框架实现

    1. 基本流程

    url.py

    1
    2
    3
    4
    5
    6
    from django.conf.urls import url, include
    from web.views.s1_api import TestView
     
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]

    views.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from rest_framework.views import APIView
    from rest_framework.response import Response
     
     
    class TestView(APIView):
        def dispatch(self, request, *args, **kwargs):
            """
            请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
             
            注意:APIView中的dispatch方法有好多好多的功能
            """
            return super().dispatch(request, *args, **kwargs)
     
        def get(self, request, *args, **kwargs):
            return Response('GET请求,响应内容')
     
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
     
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')

    上述是rest framework框架基本流程,重要的功能是在APIView的dispatch中触发。

    2.  认证和授权

    a. 用户url传入的token认证

    from django.conf.urls import url, include
    from web.viewsimport TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
            
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            val = request.query_params.get('token')
            if val not in token_list:
                raise exceptions.AuthenticationFailed("用户认证失败")
    
            return ('登录用户', '用户token')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            # 验证失败时,返回的响应头WWW-Authenticate对应的值
            pass
    
    
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    b. 请求头认证

    from django.conf.urls import url, include
    from web.viewsimport TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER()
                    else:
                        self.user = None
            
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            auth = request.META.get('HTTP_AUTHORIZATION', b'')
            if auth:
                auth = auth.encode('utf-8')
            auth = auth.split()
            if not auth or auth[0].lower() != b'basic':
                raise exceptions.AuthenticationFailed('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            if username == 'alex' and password == '123':
                return ('登录用户', '用户token')
            else:
                raise exceptions.AuthenticationFailed('用户名或密码错误')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            return 'Basic realm=api'
    
    
    class TestView(APIView):
        authentication_classes = [TestAuthentication, ]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    c. 多个认证规则

    from django.conf.urls import url, include
    from web.views.s2_auth import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class Test1Authentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                    else:
                        self.user = None
    
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            import base64
            auth = request.META.get('HTTP_AUTHORIZATION', b'')
            if auth:
                auth = auth.encode('utf-8')
            else:
                return None
            print(auth,'xxxx')
            auth = auth.split()
            if not auth or auth[0].lower() != b'basic':
                raise exceptions.AuthenticationFailed('验证失败')
            if len(auth) != 2:
                raise exceptions.AuthenticationFailed('验证失败')
            username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':')
            if username == 'alex' and password == '123':
                return ('登录用户', '用户token')
            else:
                raise exceptions.AuthenticationFailed('用户名或密码错误')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            # return 'Basic realm=api'
            pass
    
    class Test2Authentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                    else:
                        self.user = None
            
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            val = request.query_params.get('token')
            if val not in token_list:
                raise exceptions.AuthenticationFailed("用户认证失败")
    
            return ('登录用户', '用户token')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            pass
    
    
    class TestView(APIView):
        authentication_classes = [Test1Authentication, Test2Authentication]
        permission_classes = []
    
        def get(self, request, *args, **kwargs):
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    d. 认证和权限

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.permissions import BasePermission
    
    from rest_framework.request import Request
    from rest_framework import exceptions
    
    token_list = [
        'sfsfss123kuf3j123',
        'asijnfowerkkf9812',
    ]
    
    
    class TestAuthentication(BaseAuthentication):
        def authenticate(self, request):
            """
            用户认证,如果验证成功后返回元组: (用户,用户Token)
            :param request: 
            :return: 
                None,表示跳过该验证;
                    如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
                    self._authenticator = None
                    if api_settings.UNAUTHENTICATED_USER:
                        self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户
                    else:
                        self.user = None
            
                    if api_settings.UNAUTHENTICATED_TOKEN:
                        self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None
                    else:
                        self.auth = None
                (user,token)表示验证通过并设置用户名和Token;
                AuthenticationFailed异常
            """
            val = request.query_params.get('token')
            if val not in token_list:
                raise exceptions.AuthenticationFailed("用户认证失败")
    
            return ('登录用户', '用户token')
    
        def authenticate_header(self, request):
            """
            Return a string to be used as the value of the `WWW-Authenticate`
            header in a `401 Unauthenticated` response, or `None` if the
            authentication scheme should return `403 Permission Denied` responses.
            """
            pass
    
    
    class TestPermission(BasePermission):
        message = "权限验证失败"
    
        def has_permission(self, request, view):
            """
            判断是否有权限访问当前请求
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True
    
        # GenericAPIView中get_object时调用
        def has_object_permission(self, request, view, obj):
            """
            视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证
            Return `True` if permission is granted, `False` otherwise.
            :param request: 
            :param view: 
            :param obj: 
            :return: True有权限;False无权限
            """
            if request.user == "管理员":
                return True
    
    
    class TestView(APIView):
        # 认证的动作是由request.user触发
        authentication_classes = [TestAuthentication, ]
    
        # 权限
        # 循环执行所有的权限
        permission_classes = [TestPermission, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    e. 全局使用

    上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        "DEFAULT_AUTHENTICATION_CLASSES": [
            "web.utils.TestAuthentication",
        ],
        "DEFAULT_PERMISSION_CLASSES": [
            "web.utils.TestPermission",
        ],
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    class TestView(APIView):
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    3. 用户访问次数/频率限制

    a. 基于用户IP限制访问频率

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import time
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from rest_framework import exceptions
    from rest_framework.throttling import BaseThrottle
    from rest_framework.settings import api_settings
    
    # 保存访问记录
    RECORD = {
        '用户IP': [12312139, 12312135, 12312133, ]
    }
    
    
    class TestThrottle(BaseThrottle):
        ctime = time.time
    
        def get_ident(self, request):
            """
            根据用户IP和代理IP,当做请求者的唯一IP
            Identify the machine making the request by parsing HTTP_X_FORWARDED_FOR
            if present and number of proxies is > 0. If not use all of
            HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR.
            """
            xff = request.META.get('HTTP_X_FORWARDED_FOR')
            remote_addr = request.META.get('REMOTE_ADDR')
            num_proxies = api_settings.NUM_PROXIES
    
            if num_proxies is not None:
                if num_proxies == 0 or xff is None:
                    return remote_addr
                addrs = xff.split(',')
                client_addr = addrs[-min(num_proxies, len(addrs))]
                return client_addr.strip()
    
            return ''.join(xff.split()) if xff else remote_addr
    
        def allow_request(self, request, view):
            """
            是否仍然在允许范围内
            Return `True` if the request should be allowed, `False` otherwise.
            :param request: 
            :param view: 
            :return: True,表示可以通过;False表示已超过限制,不允许访问
            """
            # 获取用户唯一标识(如:IP)
    
            # 允许一分钟访问10次
            num_request = 10
            time_request = 60
    
            now = self.ctime()
            ident = self.get_ident(request)
            self.ident = ident
            if ident not in RECORD:
                RECORD[ident] = [now, ]
                return True
            history = RECORD[ident]
            while history and history[-1] <= now - time_request:
                history.pop()
            if len(history) < num_request:
                history.insert(0, now)
                return True
    
        def wait(self):
            """
            多少秒后可以允许继续访问
            Optionally, return a recommended number of seconds to wait before
            the next request.
            """
            last_time = RECORD[self.ident][0]
            now = self.ctime()
            return int(60 + last_time - now)
    
    
    class TestView(APIView):
        throttle_classes = [TestThrottle, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
        def throttled(self, request, wait):
            """
            访问次数被限制时,定制错误信息
            """
    
            class Throttled(exceptions.Throttled):
                default_detail = '请求被限制.'
                extra_detail_singular = '请 {wait} 秒之后再重试.'
                extra_detail_plural = '请 {wait} 秒之后再重试.'
    
            raise Throttled(wait)
    views.py

    b. 基于用户IP显示访问频率(利于Django缓存)

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {
            'test_scope': '10/m',
        },
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from rest_framework import exceptions
    from rest_framework.throttling import SimpleRateThrottle
    
    
    class TestThrottle(SimpleRateThrottle):
    
        # 配置文件定义的显示频率的Key
        scope = "test_scope"
    
        def get_cache_key(self, request, view):
            """
            Should return a unique cache-key which can be used for throttling.
            Must be overridden.
    
            May return `None` if the request should not be throttled.
            """
            if not request.user:
                ident = self.get_ident(request)
            else:
                ident = request.user
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }
    
    
    class TestView(APIView):
        throttle_classes = [TestThrottle, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
        def throttled(self, request, wait):
            """
            访问次数被限制时,定制错误信息
            """
    
            class Throttled(exceptions.Throttled):
                default_detail = '请求被限制.'
                extra_detail_singular = '请 {wait} 秒之后再重试.'
                extra_detail_plural = '请 {wait} 秒之后再重试.'
    
            raise Throttled(wait)
    views.py

    c. view中限制请求频率

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {
            'xxxxxx': '10/m',
        },
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from rest_framework import exceptions
    from rest_framework.throttling import ScopedRateThrottle
    
    
    # 继承 ScopedRateThrottle
    class TestThrottle(ScopedRateThrottle):
    
        def get_cache_key(self, request, view):
            """
            Should return a unique cache-key which can be used for throttling.
            Must be overridden.
    
            May return `None` if the request should not be throttled.
            """
            if not request.user:
                ident = self.get_ident(request)
            else:
                ident = request.user
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }
    
    
    class TestView(APIView):
        throttle_classes = [TestThrottle, ]
    
        # 在settings中获取 xxxxxx 对应的频率限制值
        throttle_scope = "xxxxxx"
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    
        def throttled(self, request, wait):
            """
            访问次数被限制时,定制错误信息
            """
    
            class Throttled(exceptions.Throttled):
                default_detail = '请求被限制.'
                extra_detail_singular = '请 {wait} 秒之后再重试.'
                extra_detail_plural = '请 {wait} 秒之后再重试.'
    
            raise Throttled(wait)
    views.py

    d. 匿名时用IP限制+登录时用Token限制

    REST_FRAMEWORK = {
        'UNAUTHENTICATED_USER': None,
        'UNAUTHENTICATED_TOKEN': None,
        'DEFAULT_THROTTLE_RATES': {
            'luffy_anon': '10/m',
            'luffy_user': '20/m',
        },
    }
    settings.py
    from django.conf.urls import url, include
    from web.views.s3_throttling import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from rest_framework.throttling import SimpleRateThrottle
    
    
    class LuffyAnonRateThrottle(SimpleRateThrottle):
        """
        匿名用户,根据IP进行限制
        """
        scope = "luffy_anon"
    
        def get_cache_key(self, request, view):
            # 用户已登录,则跳过 匿名频率限制
            if request.user:
                return None
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    
    
    class LuffyUserRateThrottle(SimpleRateThrottle):
        """
        登录用户,根据用户token限制
        """
        scope = "luffy_user"
    
        def get_ident(self, request):
            """
            认证成功时:request.user是用户对象;request.auth是token对象
            :param request: 
            :return: 
            """
            # return request.auth.token
            return "user_token"
    
        def get_cache_key(self, request, view):
            """
            获取缓存key
            :param request: 
            :param view: 
            :return: 
            """
            # 未登录用户,则跳过 Token限制
            if not request.user:
                return None
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': self.get_ident(request)
            }
    
    
    class TestView(APIView):
        throttle_classes = [LuffyUserRateThrottle, LuffyAnonRateThrottle, ]
    
        def get(self, request, *args, **kwargs):
            # self.dispatch
            print(request.user)
            print(request.auth)
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    e. 全局使用

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES': [
            'api.utils.throttles.throttles.LuffyAnonRateThrottle',
            'api.utils.throttles.throttles.LuffyUserRateThrottle',
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '10/day',
            'user': '10/day',
            'luffy_anon': '10/m',
            'luffy_user': '20/m',
        },
    }
    settings

    4. 版本

    a. 基于url的get传参方式

    如:/users?version=v1

    REST_FRAMEWORK = {
        'DEFAULT_VERSION': 'v1',            # 默认版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
        'VERSION_PARAM': 'version'          # URL中获取值的key
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view(),name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import QueryParameterVersioning
    
    
    class TestView(APIView):
        versioning_class = QueryParameterVersioning
    
        def get(self, request, *args, **kwargs):
    
            # 获取版本
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
    
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    b. 基于url的正则方式

    如:/v1/users/

    REST_FRAMEWORK = {
        'DEFAULT_VERSION': 'v1',            # 默认版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
        'VERSION_PARAM': 'version'          # URL中获取值的key
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import URLPathVersioning
    
    
    class TestView(APIView):
        versioning_class = URLPathVersioning
    
        def get(self, request, *args, **kwargs):
            # 获取版本
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
    
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    c. 基于 accept 请求头方式

    如:Accept: application/json; version=1.0

    REST_FRAMEWORK = {
        'DEFAULT_VERSION': 'v1',            # 默认版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
        'VERSION_PARAM': 'version'          # URL中获取值的key
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import AcceptHeaderVersioning
    
    
    class TestView(APIView):
        versioning_class = AcceptHeaderVersioning
    
        def get(self, request, *args, **kwargs):
            # 获取版本 HTTP_ACCEPT头
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    d. 基于主机名方法

    如:v1.example.com

    ALLOWED_HOSTS = ['*']
    REST_FRAMEWORK = {
        'DEFAULT_VERSION': 'v1',  # 默认版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
        'VERSION_PARAM': 'version'  # URL中获取值的key
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import HostNameVersioning
    
    
    class TestView(APIView):
        versioning_class = HostNameVersioning
    
        def get(self, request, *args, **kwargs):
            # 获取版本
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    e. 基于django路由系统的namespace

    如:example.com/v1/users/

    REST_FRAMEWORK = {
        'DEFAULT_VERSION': 'v1',  # 默认版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
        'VERSION_PARAM': 'version'  # URL中获取值的key
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'^v1/', ([
                          url(r'test/', TestView.as_view(), name='test'),
                      ], None, 'v1')),
        url(r'^v2/', ([
                          url(r'test/', TestView.as_view(), name='test'),
                      ], None, 'v2')),
    
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.versioning import NamespaceVersioning
    
    
    class TestView(APIView):
        versioning_class = NamespaceVersioning
    
        def get(self, request, *args, **kwargs):
            # 获取版本
            print(request.version)
            # 获取版本管理的类
            print(request.versioning_scheme)
            # 反向生成URL
            reverse_url = request.versioning_scheme.reverse('test', request=request)
            print(reverse_url)
    
            return Response('GET请求,响应内容')
    
        def post(self, request, *args, **kwargs):
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    f. 全局使用

    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
        'DEFAULT_VERSION': 'v1',
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        'VERSION_PARAM': 'version' 
    }
    settings.py

    5. 解析器(parser) 

    根据请求头 content-type 选择对应的解析器就请求体内容进行处理。

    a. 仅处理请求头content-type为application/json的请求体

    from django.conf.urls import url, include
    from web.views.s5_parser import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import JSONParser
    
    
    class TestView(APIView):
        parser_classes = [JSONParser, ]
    
        def post(self, request, *args, **kwargs):
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
    
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
    
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    b. 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import FormParser
    
    
    class TestView(APIView):
        parser_classes = [FormParser, ]
    
        def post(self, request, *args, **kwargs):
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
    
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
    
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    c. 仅处理请求头content-type为multipart/form-data的请求体

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import MultiPartParser
    
    
    class TestView(APIView):
        parser_classes = [MultiPartParser, ]
    
        def post(self, request, *args, **kwargs):
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="http://127.0.0.1:8000/test/" method="post" enctype="multipart/form-data">
        <input type="text" name="user" />
        <input type="file" name="img">
    
        <input type="submit" value="提交">
    
    </form>
    </body>
    </html>
    upload.html

    d. 仅上传文件

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'test/(?P<filename>[^/]+)', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import FileUploadParser
    
    
    class TestView(APIView):
        parser_classes = [FileUploadParser, ]
    
        def post(self, request, filename, *args, **kwargs):
            print(filename)
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="http://127.0.0.1:8000/test/f1.numbers" method="post" enctype="multipart/form-data">
        <input type="text" name="user" />
        <input type="file" name="img">
    
        <input type="submit" value="提交">
    
    </form>
    </body>
    </html>
    upload.html

    e. 同时多个Parser

    当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser

    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.request import Request
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
    
    
    class TestView(APIView):
        parser_classes = [JSONParser, FormParser, MultiPartParser, ]
    
        def post(self, request, *args, **kwargs):
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    f. 全局使用

    REST_FRAMEWORK = {
        'DEFAULT_PARSER_CLASSES':[
            'rest_framework.parsers.JSONParser'
            'rest_framework.parsers.FormParser'
            'rest_framework.parsers.MultiPartParser'
        ]
    
    }
    settings.py
    from django.conf.urls import url, include
    from web.views import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    
    class TestView(APIView):
        def post(self, request, *args, **kwargs):
            print(request.content_type)
    
            # 获取请求的值,并使用对应的JSONParser进行处理
            print(request.data)
            # application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
            print(request.POST)
            print(request.FILES)
            return Response('POST请求,响应内容')
    
        def put(self, request, *args, **kwargs):
            return Response('PUT请求,响应内容')
    views.py

    注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取

    6. 序列化

    序列化用于对用户请求数据进行验证和数据进行序列化。

    a. 自定义字段

    from django.conf.urls import url, include
    from web.views.s6_serializers import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from .. import models
    
    
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = base
    
        def __call__(self, value):
            if value != self.base:
                message = 'This field must be %s.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    
    class UserSerializer(serializers.Serializer):
        ut_title = serializers.CharField(source='ut.title')
        user = serializers.CharField(min_length=6)
        pwd = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')])
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
    
            # 序列化,将数据库查询字段序列化为字典
            data_list = models.UserInfo.objects.all()
            ser = UserSerializer(instance=data_list, many=True)
            #
            # obj = models.UserInfo.objects.all().first()
            # ser = UserSerializer(instance=obj, many=False)
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            # 验证,对请求发来的数据进行验证
            ser = UserSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
    
            return Response('POST请求,响应内容')
    views.py

    b. 基于Model自动生成字段

    from django.conf.urls import url, include
    from web.views.s6_serializers import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from .. import models
    
    
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = str(base)
    
        def __call__(self, value):
            if value != self.base:
                message = 'This field must be %s.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    class ModelUserSerializer(serializers.ModelSerializer):
    
        user = serializers.CharField(max_length=32)
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            # fields = ['user', 'pwd', 'ut']
            depth = 2
            extra_kwargs = {'user': {'min_length': 6}, 'pwd': {'validators': [PasswordValidator(666), ]}}
            # read_only_fields = ['user']
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
    
            # 序列化,将数据库查询字段序列化为字典
            data_list = models.UserInfo.objects.all()
            ser = ModelUserSerializer(instance=data_list, many=True)
            #
            # obj = models.UserInfo.objects.all().first()
            # ser = UserSerializer(instance=obj, many=False)
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            # 验证,对请求发来的数据进行验证
            print(request.data)
            ser = ModelUserSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
    
            return Response('POST请求,响应内容')
    views.py

    c. 生成URL

    from django.conf.urls import url, include
    from web.views.s6_serializers import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
        url(r'detail/(?P<pk>d+)/', TestView.as_view(), name='detail'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from .. import models
    
    
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = str(base)
    
        def __call__(self, value):
            if value != self.base:
                message = 'This field must be %s.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    
    class ModelUserSerializer(serializers.ModelSerializer):
        ut = serializers.HyperlinkedIdentityField(view_name='detail')
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
            extra_kwargs = {
                'user': {'min_length': 6},
                'pwd': {'validators': [PasswordValidator(666),]},
            }
    
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
    
            # 序列化,将数据库查询字段序列化为字典
            data_list = models.UserInfo.objects.all()
            ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
            #
            # obj = models.UserInfo.objects.all().first()
            # ser = UserSerializer(instance=obj, many=False)
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            # 验证,对请求发来的数据进行验证
            print(request.data)
            ser = ModelUserSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
    
            return Response('POST请求,响应内容')
    views.py

    d. 自动生成URL

    from django.conf.urls import url, include
    from web.views.s6_serializers import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view(), name='test'),
        url(r'detail/(?P<pk>d+)/', TestView.as_view(), name='xxxx'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from .. import models
    
    
    class PasswordValidator(object):
        def __init__(self, base):
            self.base = str(base)
    
        def __call__(self, value):
            if value != self.base:
                message = 'This field must be %s.' % self.base
                raise serializers.ValidationError(message)
    
        def set_context(self, serializer_field):
            """
            This hook is called by the serializer instance,
            prior to the validation call being made.
            """
            # 执行验证之前调用,serializer_fields是当前字段对象
            pass
    
    
    class ModelUserSerializer(serializers.HyperlinkedModelSerializer):
        ll = serializers.HyperlinkedIdentityField(view_name='xxxx')
        tt = serializers.CharField(required=False)
    
        class Meta:
            model = models.UserInfo
            fields = "__all__"
            list_serializer_class = serializers.ListSerializer
    
            extra_kwargs = {
                'user': {'min_length': 6},
                'pwd': {'validators': [PasswordValidator(666), ]},
                'url': {'view_name': 'xxxx'},
                'ut': {'view_name': 'xxxx'},
            }
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
            # # 序列化,将数据库查询字段序列化为字典
            data_list = models.UserInfo.objects.all()
            ser = ModelUserSerializer(instance=data_list, many=True, context={'request': request})
            # # 如果Many=True
            # # 或
            # # obj = models.UserInfo.objects.all().first()
            # # ser = UserSerializer(instance=obj, many=False)
            return Response(ser.data)
    
        def post(self, request, *args, **kwargs):
            # 验证,对请求发来的数据进行验证
            print(request.data)
            ser = ModelUserSerializer(data=request.data)
            if ser.is_valid():
                print(ser.validated_data)
            else:
                print(ser.errors)
    
            return Response('POST请求,响应内容')
    views.py

    7. 分页

    a. 根据页码进行分页

    from django.conf.urls import url, include
    from rest_framework import routers
    from web.views import s9_pagination
    
    urlpatterns = [
        url(r'^test/', s9_pagination.UserViewSet.as_view()),
    ]
    urs.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework import serializers
    from .. import models
    
    from rest_framework.pagination import PageNumberPagination
    
    
    class StandardResultsSetPagination(PageNumberPagination):
        # 默认每页显示的数据条数
        page_size = 1
        # 获取URL参数中设置的每页显示数据条数
        page_size_query_param = 'page_size'
    
        # 获取URL参数中传入的页码key
        page_query_param = 'page'
    
        # 最大支持的每页显示的数据条数
        max_page_size = 1
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(APIView):
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().order_by('-id')
    
            # 实例化分页对象,获取数据库中的分页数据
            paginator = StandardResultsSetPagination()
            page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
    
            # 序列化对象
            serializer = UserSerializer(page_user_list, many=True)
    
            # 生成分页和数据
            response = paginator.get_paginated_response(serializer.data)
            return response
    views.py

    b. 位置和个数进行分页

    from django.conf.urls import url, include
    from web.views import s9_pagination
    
    urlpatterns = [
        url(r'^test/', s9_pagination.UserViewSet.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework import serializers
    from .. import models
    
    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
    
    
    class StandardResultsSetPagination(LimitOffsetPagination):
        # 默认每页显示的数据条数
        default_limit = 10
        # URL中传入的显示数据条数的参数
        limit_query_param = 'limit'
        # URL中传入的数据位置的参数
        offset_query_param = 'offset'
        # 最大每页显得条数
        max_limit = None
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(APIView):
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().order_by('-id')
    
            # 实例化分页对象,获取数据库中的分页数据
            paginator = StandardResultsSetPagination()
            page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
    
            # 序列化对象
            serializer = UserSerializer(page_user_list, many=True)
    
            # 生成分页和数据
            response = paginator.get_paginated_response(serializer.data)
            return response
    views.py

    c. 游标分页

    from django.conf.urls import url, include
    from web.views import s9_pagination
    
    urlpatterns = [
        url(r'^test/', s9_pagination.UserViewSet.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework import serializers
    from .. import models
    
    from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
    
    
    class StandardResultsSetPagination(CursorPagination):
        # URL传入的游标参数
        cursor_query_param = 'cursor'
        # 默认每页显示的数据条数
        page_size = 2
        # URL传入的每页显示条数的参数
        page_size_query_param = 'page_size'
        # 每页显示数据最大条数
        max_page_size = 1000
    
        # 根据ID从大到小排列
        ordering = "id"
    
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(APIView):
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().order_by('-id')
    
            # 实例化分页对象,获取数据库中的分页数据
            paginator = StandardResultsSetPagination()
            page_user_list = paginator.paginate_queryset(user_list, self.request, view=self)
    
            # 序列化对象
            serializer = UserSerializer(page_user_list, many=True)
    
            # 生成分页和数据
            response = paginator.get_paginated_response(serializer.data)
            return response
    views.py

    8. 路由系统

    a. 自定义路由

    from django.conf.urls import url, include
    from web.views import s11_render
    
    urlpatterns = [
        url(r'^test/$', s11_render.TestView.as_view()),
        url(r'^test.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()),
        url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()),
        url(r'^test/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view())
    ]
    urls.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .. import models
    
    
    class TestView(APIView):
        def get(self, request, *args, **kwargs):
            print(kwargs)
            print(self.renderer_classes)
            return Response('...')
    views.py

    b. 半自动路由

    from django.conf.urls import url, include
    from web.views import s10_generic
    
    urlpatterns = [
        url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
        url(r'^test/(?P<pk>d+)/$', s10_generic.UserViewSet.as_view(
            {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.viewsets import ModelViewSet
    from rest_framework import serializers
    from .. import models
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(ModelViewSet):
        queryset = models.UserInfo.objects.all()
        serializer_class = UserSerializer
    views.py

    c. 全自动路由

    from django.conf.urls import url, include
    from rest_framework import routers
    from web.views import s10_generic
    
    
    router = routers.DefaultRouter()
    router.register(r'users', s10_generic.UserViewSet)
    
    urlpatterns = [
        url(r'^', include(router.urls)),
    ]
    urls.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework import serializers
    from .. import models
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(ModelViewSet):
        queryset = models.UserInfo.objects.all()
        serializer_class = UserSerializer
    views.py

    9. 视图

    a. GenericViewSet

    from django.conf.urls import url, include
    from web.views.s7_viewset import TestView
    
    urlpatterns = [
        url(r'test/', TestView.as_view({'get':'list'}), name='test'),
        url(r'detail/(?P<pk>d+)/', TestView.as_view({'get':'list'}), name='xxxx'),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework import viewsets
    from rest_framework.response import Response
    
    
    class TestView(viewsets.GenericViewSet):
        def list(self, request, *args, **kwargs):
            return Response('...')
    
        def add(self, request, *args, **kwargs):
            pass
    
        def delete(self, request, *args, **kwargs):
            pass
    
        def edit(self, request, *args, **kwargs):
            pass
    views.py

    b. ModelViewSet(自定义URL)

    from django.conf.urls import url, include
    from web.views import s10_generic
    
    urlpatterns = [
        url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})),
        url(r'^test/(?P<pk>d+)/$', s10_generic.UserViewSet.as_view(
            {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.viewsets import ModelViewSet
    from rest_framework import serializers
    from .. import models
    
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class UserViewSet(ModelViewSet):
        queryset = models.UserInfo.objects.all()
        serializer_class = UserSerializer
    views.py

    c. ModelViewSet(rest framework路由)

    from django.conf.urls import url, include
    from rest_framework import routers
    from app01 import views
    
    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)
    router.register(r'groups', views.GroupViewSet)
    
    # Wire up our API using automatic URL routing.
    # Additionally, we include login URLs for the browsable API.
    urlpatterns = [
        url(r'^', include(router.urls)),
    ]
    urls.py
    from rest_framework import viewsets
    from rest_framework import serializers
    
    
    class UserSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = models.User
            fields = ('url', 'username', 'email', 'groups')
    
    
    class GroupSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = models.Group
            fields = ('url', 'name')
            
    class UserViewSet(viewsets.ModelViewSet):
        """
        API endpoint that allows users to be viewed or edited.
        """
        queryset = User.objects.all().order_by('-date_joined')
        serializer_class = UserSerializer
    
    
    class GroupViewSet(viewsets.ModelViewSet):
        """
        API endpoint that allows groups to be viewed or edited.
        """
        queryset = Group.objects.all()
        serializer_class = GroupSerializer
    views.py

    10. 渲染器

    根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件。
    用户请求URL:

    • http://127.0.0.1:8000/test/?format=json
    • http://127.0.0.1:8000/test.json

    用户请求头:

    • Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

    a. json

    访问URL:

    • http://127.0.0.1:8000/test/?format=json
    • http://127.0.0.1:8000/test.json
    • http://127.0.0.1:8000/test/ 
    from django.conf.urls import url, include
    from web.views import s11_render
    
    urlpatterns = [
        url(r'^test/$', s11_render.TestView.as_view()),
        url(r'^test.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    
    from rest_framework.renderers import JSONRenderer
    
    from .. import models
    
    
    class TestSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class TestView(APIView):
        renderer_classes = [JSONRenderer, ]
    
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all()
            ser = TestSerializer(instance=user_list, many=True)
            return Response(ser.data)
    views.py

    b. 表格

    访问URL:

    • http://127.0.0.1:8000/test/?format=admin
    • http://127.0.0.1:8000/test.admin
    • http://127.0.0.1:8000/test/ 
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    
    from rest_framework.renderers import AdminRenderer
    
    from .. import models
    
    
    class TestSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class TestView(APIView):
        renderer_classes = [AdminRenderer, ]
    
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all()
            ser = TestSerializer(instance=user_list, many=True)
            return Response(ser.data)
    views.py

    c. Form表单

    访问URL:

    • http://127.0.0.1:8000/test/?format=form
    • http://127.0.0.1:8000/test.form
    • http://127.0.0.1:8000/test/ 
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    
    from rest_framework.renderers import JSONRenderer
    from rest_framework.renderers import AdminRenderer
    from rest_framework.renderers import HTMLFormRenderer
    
    from .. import models
    
    
    class TestSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class TestView(APIView):
        renderer_classes = [HTMLFormRenderer, ]
    
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().first()
            ser = TestSerializer(instance=user_list, many=False)
            return Response(ser.data)
    views.py

    d. 自定义显示模板

    访问URL:

    • http://127.0.0.1:8000/test/?format=html
    • http://127.0.0.1:8000/test.html
    • http://127.0.0.1:8000/test/ 
    from django.conf.urls import url, include
    from web.views import s11_render
    
    urlpatterns = [
        url(r'^test/$', s11_render.TestView.as_view()),
        url(r'^test.(?P<format>[a-z0-9]+)', s11_render.TestView.as_view()),
    ]
    urls.py
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from rest_framework.renderers import TemplateHTMLRenderer
    
    from .. import models
    
    
    class TestSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class TestView(APIView):
        renderer_classes = [TemplateHTMLRenderer, ]
    
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().first()
            ser = TestSerializer(instance=user_list, many=False)
            return Response(ser.data, template_name='user_detail.html')
    views.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {{ user }}
        {{ pwd }}
        {{ ut }}
    </body>
    </html>
    userdetail.html

    e. 浏览器格式API+JSON

    访问URL:

    • http://127.0.0.1:8000/test/?format=api
    • http://127.0.0.1:8000/test.api
    • http://127.0.0.1:8000/test/ 
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    
    from rest_framework.renderers import JSONRenderer
    from rest_framework.renderers import BrowsableAPIRenderer
    
    from .. import models
    
    
    class TestSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.UserInfo
            fields = "__all__"
    
    
    class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
        def get_default_renderer(self, view):
            return JSONRenderer()
    
    
    class TestView(APIView):
        renderer_classes = [CustomBrowsableAPIRenderer, ]
    
        def get(self, request, *args, **kwargs):
            user_list = models.UserInfo.objects.all().first()
            ser = TestSerializer(instance=user_list, many=False)
            return Response(ser.data, template_name='user_detail.html')
    views.py

    注意:如果同时多个存在时,自动根据URL后缀来选择渲染器。

    APIView源码分析

    class APIView(View):
    
        # The following policies may be set at either globally, or per-view.
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        # 解析器类s
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        # 认证类s
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        # 限制访问类s
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
        # 权限类s
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
        metadata_class = api_settings.DEFAULT_METADATA_CLASS
        # 版本类
        versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    
        # Allow dependency injection of other settings to make testing easier.
        settings = api_settings
    
        schema = AutoSchema()
    
        @classmethod
        def as_view(cls, **initkwargs):
            """
            Store the original class on the view function.
    
            This allows us to discover information about the view when we do URL
            reverse lookups.  Used for breadcrumb generation.
            """
            if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
                def force_evaluation():
                    raise RuntimeError(
                        'Do not evaluate the `.queryset` attribute directly, '
                        'as the result will be cached and reused between requests. '
                        'Use `.all()` or call `.get_queryset()` instead.'
                    )
                cls.queryset._fetch_all = force_evaluation
    
            view = super(APIView, cls).as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
    
            # Note: session based authentication is explicitly CSRF validated,
            # all other authentication is CSRF exempt.
            return csrf_exempt(view)
    
        @property
        def allowed_methods(self):
            """
            Wrap Django's private `_allowed_methods` interface in a public property.
            """
            return self._allowed_methods()
    
        @property
        def default_response_headers(self):
            headers = {
                'Allow': ', '.join(self.allowed_methods),
            }
            if len(self.renderer_classes) > 1:
                headers['Vary'] = 'Accept'
            return headers
    
        def http_method_not_allowed(self, request, *args, **kwargs):
            """
            If `request.method` does not correspond to a handler method,
            determine what kind of exception to raise.
            """
            raise exceptions.MethodNotAllowed(request.method)
    
        def permission_denied(self, request, message=None):
            """
            If request is not permitted, determine what kind of exception to raise.
            """
            if request.authenticators and not request.successful_authenticator:
                raise exceptions.NotAuthenticated()
            raise exceptions.PermissionDenied(detail=message)
    
        def throttled(self, request, wait):
            """
            If request is throttled, determine what kind of exception to raise.
            """
            raise exceptions.Throttled(wait)
    
        def get_authenticate_header(self, request):
            """
            If a request is unauthenticated, determine the WWW-Authenticate
            header to use for 401 responses, if any.
            """
            authenticators = self.get_authenticators()
            if authenticators:
                return authenticators[0].authenticate_header(request)
    
        def get_parser_context(self, http_request):
            """
            Returns a dict that is passed through to Parser.parse(),
            as the `parser_context` keyword argument.
            """
            # Note: Additionally `request` and `encoding` will also be added
            #       to the context by the Request object.
            return {
                'view': self,
                'args': getattr(self, 'args', ()),
                'kwargs': getattr(self, 'kwargs', {})
            }
    
        def get_renderer_context(self):
            """
            Returns a dict that is passed through to Renderer.render(),
            as the `renderer_context` keyword argument.
            """
            # Note: Additionally 'response' will also be added to the context,
            #       by the Response object.
            return {
                'view': self,
                'args': getattr(self, 'args', ()),
                'kwargs': getattr(self, 'kwargs', {}),
                'request': getattr(self, 'request', None)
            }
    
        def get_exception_handler_context(self):
            """
            Returns a dict that is passed through to EXCEPTION_HANDLER,
            as the `context` argument.
            """
            return {
                'view': self,
                'args': getattr(self, 'args', ()),
                'kwargs': getattr(self, 'kwargs', {}),
                'request': getattr(self, 'request', None)
            }
    
        def get_view_name(self):
            """
            Return the view name, as used in OPTIONS responses and in the
            browsable API.
            """
            func = self.settings.VIEW_NAME_FUNCTION
            return func(self.__class__, getattr(self, 'suffix', None))
    
        def get_view_description(self, html=False):
            """
            Return some descriptive text for the view, as used in OPTIONS responses
            and in the browsable API.
            """
            func = self.settings.VIEW_DESCRIPTION_FUNCTION
            return func(self.__class__, html)
    
        # API policy instantiation methods
    
        def get_format_suffix(self, **kwargs):
            """
            Determine if the request includes a '.json' style format suffix
            """
            if self.settings.FORMAT_SUFFIX_KWARG:
                return kwargs.get(self.settings.FORMAT_SUFFIX_KWARG)
    
        def get_renderers(self):
            """
            Instantiates and returns the list of renderers that this view can use.
            """
            return [renderer() for renderer in self.renderer_classes]
    
        def get_parsers(self):
            """
            Instantiates and returns the list of parsers that this view can use.
            """
            return [parser() for parser in self.parser_classes]
    
        def get_authenticators(self):
            """
            Instantiates and returns the list of authenticators that this view can use.
            """
            return [auth() for auth in self.authentication_classes]
    
        def get_permissions(self):
            """
            Instantiates and returns the list of permissions that this view requires.
            """
            return [permission() for permission in self.permission_classes]
    
        def get_throttles(self):
            """
            Instantiates and returns the list of throttles that this view uses.
            """
            return [throttle() for throttle in self.throttle_classes]
    
        def get_content_negotiator(self):
            """
            Instantiate and return the content negotiation class to use.
            """
            if not getattr(self, '_negotiator', None):
                self._negotiator = self.content_negotiation_class()
            return self._negotiator
    
        def get_exception_handler(self):
            """
            Returns the exception handler that this view uses.
            """
            return self.settings.EXCEPTION_HANDLER
    
        # API policy implementation methods
    
        def perform_content_negotiation(self, request, force=False):
            """
            Determine which renderer and media type to use render the response.
            """
            renderers = self.get_renderers()
            conneg = self.get_content_negotiator()
    
            try:
                return conneg.select_renderer(request, renderers, self.format_kwarg)
            except Exception:
                if force:
                    return (renderers[0], renderers[0].media_type)
                raise
    
        def perform_authentication(self, request):
            """
            Perform authentication on the incoming request.
    
            Note that if you override this and simply 'pass', then authentication
            will instead be performed lazily, the first time either
            `request.user` or `request.auth` is accessed.
            """
            request.user
    
        def check_permissions(self, request):
            """
            Check if the request should be permitted.
            Raises an appropriate exception if the request is not permitted.
            """
            for permission in self.get_permissions():
                if not permission.has_permission(request, self):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )
    
        def check_object_permissions(self, request, obj):
            """
            Check if the request should be permitted for a given object.
            Raises an appropriate exception if the request is not permitted.
            """
            for permission in self.get_permissions():
                if not permission.has_object_permission(request, self, obj):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )
    
        def check_throttles(self, request):
            """
            Check if request should be throttled.
            Raises an appropriate exception if the request is throttled.
            """
            for throttle in self.get_throttles():
                if not throttle.allow_request(request, self):
                    self.throttled(request, throttle.wait())
    
        def determine_version(self, request, *args, **kwargs):
            """
            If versioning is being used, then determine any API version for the
            incoming request. Returns a two-tuple of (version, versioning_scheme)
            """
            if self.versioning_class is None:
                return (None, None)
            scheme = self.versioning_class()
            return (scheme.determine_version(request, *args, **kwargs), scheme)
    
        # Dispatch methods
    
        def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    
        def initial(self, request, *args, **kwargs):
            """
            Runs anything that needs to occur prior to calling the method handler.
            """
            self.format_kwarg = self.get_format_suffix(**kwargs)
    
            # Perform content negotiation and store the accepted info on the request
            neg = self.perform_content_negotiation(request)
            request.accepted_renderer, request.accepted_media_type = neg
    
            # Determine the API version, if versioning is in use.
            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme
    
            # Ensure that the incoming request is permitted
            self.perform_authentication(request)
            self.check_permissions(request)
            self.check_throttles(request)
    
        def finalize_response(self, request, response, *args, **kwargs):
            """
            Returns the final response object.
            """
            # Make the error obvious if a proper response is not returned
            assert isinstance(response, HttpResponseBase), (
                'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
                'to be returned from the view, but received a `%s`'
                % type(response)
            )
    
            if isinstance(response, Response):
                if not getattr(request, 'accepted_renderer', None):
                    neg = self.perform_content_negotiation(request, force=True)
                    request.accepted_renderer, request.accepted_media_type = neg
    
                response.accepted_renderer = request.accepted_renderer
                response.accepted_media_type = request.accepted_media_type
                response.renderer_context = self.get_renderer_context()
    
            # Add new vary headers to the response instead of overwriting.
            vary_headers = self.headers.pop('Vary', None)
            if vary_headers is not None:
                patch_vary_headers(response, cc_delim_re.split(vary_headers))
    
            for key, value in self.headers.items():
                response[key] = value
    
            return response
    
        def handle_exception(self, exc):
            """
            Handle any exception that occurs, by returning an appropriate response,
            or re-raising the error.
            """
            if isinstance(exc, (exceptions.NotAuthenticated,
                                exceptions.AuthenticationFailed)):
                # WWW-Authenticate header for 401 responses, else coerce to 403
                auth_header = self.get_authenticate_header(self.request)
    
                if auth_header:
                    exc.auth_header = auth_header
                else:
                    exc.status_code = status.HTTP_403_FORBIDDEN
    
            exception_handler = self.get_exception_handler()
    
            context = self.get_exception_handler_context()
            response = exception_handler(exc, context)
    
            if response is None:
                self.raise_uncaught_exception(exc)
    
            response.exception = True
            return response
    
        def raise_uncaught_exception(self, exc):
            if settings.DEBUG:
                request = self.request
                renderer_format = getattr(request.accepted_renderer, 'format')
                use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin')
                request.force_plaintext_errors(use_plaintext_traceback)
            raise
    
            # Note: Views are made CSRF exempt from within `as_view` as to prevent
            # accidental removal of this exemption in cases where `dispatch` needs to
            # be overridden.
        def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            # ####################### 第一步 request二次封装 #######################
            """
            return Request(
                request,
                parsers=self.get_parsers(),                 解析相关 对象列表
                authenticators=self.get_authenticators(),   认证相关 对象列表
                negotiator=self.get_content_negotiator(),   选择相关 选择对象
                parser_context=parser_context               解析内容
            )
            """
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            # ####################### 第二步 初始化 #######################
            """
            2.1 获取版本
                返回(scheme.determine_version(request, *args, **kwargs), scheme)
                request.version, request.versioning_scheme =版本号,检查版本的对象
            2.2 认证    
                self.perform_authentication(request)
                调用request.user方法
            2.3 检查权限
                self.check_permissions(request)    
                    获取权限的对象列表   
                    执行对象.has_permission方法   返回True有权限,返回False没有权限,抛出异常,message定制错误信息。
            2.4 检查限制访问
                self.check_throttles(request)
                    获取限制类的对象列表
                    执行对象.allow_request(request, self)   返回True可以访问,返回False限制访问。
    
            """
            try:
                self.initial(request, *args, **kwargs)
                # ####################### 第三步 执行对应的视图函数 #######################
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    
        def options(self, request, *args, **kwargs):
            """
            Handler method for HTTP 'OPTIONS' request.
            """
            if self.metadata_class is None:
                return self.http_method_not_allowed(request, *args, **kwargs)
            data = self.metadata_class().determine_metadata(request, self)
            return Response(data, status=status.HTTP_200_OK)
    views.py

    Django Rest Framework 的的请求生命周期如下:

    HTTP请求 —> wsgi —> 中间件 —> 路由分发 —> 执行对应类的dispatch方法 —> 视图函数 —>返回 

    主要说dispatch中执行了哪些操作:

        def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            # ####################### 第一步 request二次封装 #######################
            """
            return Request(
                request,
                parsers=self.get_parsers(),                 解析相关 对象列表
                authenticators=self.get_authenticators(),   认证相关 对象列表
                negotiator=self.get_content_negotiator(),   选择相关 选择对象
                parser_context=parser_context               解析内容
            )
            """
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            # ####################### 第二步 初始化 #######################
            """
            2.1 获取版本
                返回(scheme.determine_version(request, *args, **kwargs), scheme)
                request.version, request.versioning_scheme =版本号,检查版本的对象
            2.2 认证    
                self.perform_authentication(request)
                调用request.user方法
            2.3 检查权限
                self.check_permissions(request)    
                    获取权限的对象列表   
                    执行对象.has_permission方法   返回True有权限,返回False没有权限,抛出异常,message定制错误信息。
            2.4 检查限制访问
                self.check_throttles(request)
                    获取限制类的对象列表
                    执行对象.allow_request(request, self)   返回True可以访问,返回False限制访问。
    
            """
            try:
                self.initial(request, *args, **kwargs)
                # ####################### 第三步 执行对应的视图函数 #######################
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
  • 相关阅读:
    Tensorflow遇到的问题
    IPAdr2023网络计算器IP Address Computer Edition 2023 download
    AtomCalcor 2022 原子计算器下载Atom Calcor Edition 2022 download
    js对手机软键盘的监听
    GDB 命令行调试之路(全 19)
    URLConnection 的陷阱 java_关于connection reset 服务器cgi
    详解KMP算法 另一种思路
    EF中提示一个或者多个验证出现错误,查看 EntityValidationErrors 详细信息的解决方法
    C#中数字日期转中文小写日期
    想要淘便宜淘白菜的请访问 http://31tj.com
  • 原文地址:https://www.cnblogs.com/bubu99/p/11961061.html
Copyright © 2020-2023  润新知