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)
四. 基于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()), ]
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.认证
使用方式:
创建类:继承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', } }