zoukankan      html  css  js  c++  java
  • django rest framework

    一. 什么是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。

    {
        error: "Invalid API key"
    }

    返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。

    GET /collection:返回资源对象的列表(数组)
    GET /collection/resource:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/resource:返回完整的资源对象
    PATCH /collection/resource:返回完整的资源对象
    DELETE /collection/resource:返回一个空文档

    Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

    {"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实现

    urlpatterns = [
        url(r'^users', Users.as_view()),
    ]
    路由系统
    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) 
    CBV视图

    四. 基于django rest framework实现(认证,权限,节流)

    1.基本流程

    from django.conf.urls import url, include
    from web.views.s1_api import TestView
     
    urlpatterns = [
        url(r'^test/', TestView.as_view()),
    ]
    url.py
    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请求,响应内容')
    views.py

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

    2.认证

     使用方式:

    创建类:继承BaseAuthentication; 实现:authenticate方法
    
    - 返回值:
                - None,我不管了,下一认证来执行。
                - raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions
                - (元素1,元素2)  # 元素1赋值给request.user; 元素2赋值给request.auth 

    局部:

    from rest_framework.authentication import BaseAuthentication,BasicAuthentication
    class UserInfoView(APIView):
        """
        订单相关业务
        """
        authentication_classes = [BasicAuthentication,]
        def get(self,request,*args,**kwargs):
            print(request.user)
            return HttpResponse('用户信息')

    全局:

    - 全局使用:
        REST_FRAMEWORK = {
            # 全局使用的认证类
            "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
            # "UNAUTHENTICATED_USER":lambda :"匿名用户"
            "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
            "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
        }

    源码流程:

    - dispatch
        - 封装request
            - 获取定义的认证类(全局/局部),通过列表生成时创建对象。
        - initial
            - perform_authentication
                request.user(内部循环....)

    3.权限:

    class MyPermission(object):
    
        def has_permission(self,request,view):
            if request.user.user_type != 3:
                return False
            return True
                                
    
    class OrderView(APIView):
        """
        订单相关业务(只有SVIP用户有权限)
        """
        permission_classes = [MyPermission,]
        
        def get(self,request,*args,**kwargs):
            # request.user
            # request.auth
            self.dispatch
            ret = {'code':1000,'msg':None,'data':None}
            try:
                ret['data'] = ORDER_DICT
            except Exception as e:
                pass
            return JsonResponse(ret)
    局部权限
    - 全局 
        REST_FRAMEWORK = {
            "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission']
        }
    全局权限

    使用时注意点:

    类,必须继承:BasePermission(因为规范!不写也能运行),必须实现:has_permission方法

    from rest_framework.permissions import BasePermission
    
    class SVIPPermission(BasePermission):
            # message可以给用户一些提示
        message = "必须是SVIP才能访问"
        def has_permission(self,request,view):
            if request.user.user_type != 3:
                return False
            return True

    - 返回值:
      - True, 有权访问
      - False,无权访问

    4.访问频率控制(节流)

    可以用作反爬虫,对用户的访问频率做一些限制

    import time
    VISIT_RECORD = {}
    
    class VisitThrottle(object):
        """60s内只能访问3次"""
    
        def __init__(self):
            self.history = None
    
        def allow_request(self,request,view):
            # 1. 获取用户IP
            remote_addr = request.META.get('REMOTE_ADDR')
            ctime = time.time()
            if remote_addr not in VISIT_RECORD:
                VISIT_RECORD[remote_addr] = [ctime,]
                return True
            history = VISIT_RECORD.get(remote_addr)
            self.history = history
    
            while history and history[-1] < ctime - 60:
                history.pop()
    
            if len(history) < 3:
                history.insert(0,ctime)
                return True
    
            # return True    # 表示可以继续访问
            # return False # 表示访问频率太高,被限制
    
        def wait(self):
            """
            还需要等多少秒才能访问
            :return:
            """
            ctime = time.time()
            return 60 - (ctime - self.history[-1])
    节流
    class AuthView(APIView):
        """
        用于用户登录认证
        """
        authentication_classes = []
        permission_classes = []
        throttle_classes = [VisitThrottle,]
    
        def post(self,request,*args,**kwargs):
    
    
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = "用户名或密码错误"
                # 为登录用户创建token
                token = md5(user)
                # 存在就更新,不存在就创建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '请求异常'
    
            return JsonResponse(ret)
    局部应用
    内置控制频率类:
    from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
    class VisitThrottle(SimpleRateThrottle):
        scope = "Luffy"
    
        def get_cache_key(self, request, view):
            return self.get_ident(request)
    
    
    class UserThrottle(SimpleRateThrottle):
        scope = "LuffyUser"
    
        def get_cache_key(self, request, view):
            return request.user.username
    
    
    REST_FRAMEWORK = {
        # 全局使用的认证类
        "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
        # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
        # "UNAUTHENTICATED_USER":lambda :"匿名用户"
        "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
        "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
        "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
        "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
        "DEFAULT_THROTTLE_RATES":{
            "Luffy":'3/m',
            "LuffyUser":'10/m',
        }
    }
    内置的节流类

    总结:

    a. 基本使用 
    - 类, 继承:BaseThrottle,实现:allow_request、wait
    - 类, 继承:SimpleRateThrottle,实现:get_cache_key、scope = "Luffy"(配置文件中的key)
    
    b. 局部 
    class AuthView(APIView):
        """
        用于用户登录认证
        """
        authentication_classes = []
        permission_classes = []
        throttle_classes = [VisitThrottle,] # *******************
    
        def post(self,request,*args,**kwargs):
    
            ret = {'code':1000,'msg':None}
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:
                    ret['code'] = 1001
                    ret['msg'] = "用户名或密码错误"
                # 为登录用户创建token
                token = md5(user)
                # 存在就更新,不存在就创建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '请求异常'
    
            return JsonResponse(ret)
            
    c. 全局
    REST_FRAMEWORK = {
        # 全局使用的认证类
        "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
        # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
        # "UNAUTHENTICATED_USER":lambda :"匿名用户"
        "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
        "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
        "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
        
        "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
        "DEFAULT_THROTTLE_RATES":{
            "Luffy":'3/m',
            "LuffyUser":'10/m',
        }
    }
  • 相关阅读:
    BZOJ4066 简单题(KD-Tree)
    [HAOI2006]受欢迎的牛 tarjan缩点 + 拓扑排序
    [JSOI2007]重要的城市 floyd:最短路计数
    [SDOI2017]新生舞会 0/1分数规划
    [APIO2017]商旅 0/1分数规划
    [HNOI2009]最小圈
    算法——0/1分数规划
    运动员最佳匹配问题 KM算法:带权二分图匹配
    [NOI2015]荷马史诗
    [HAOI2010]计数 数位DP+组合数
  • 原文地址:https://www.cnblogs.com/louyifei0824/p/10022486.html
Copyright © 2011-2022 走看看