REST是什么呢?它是一种架构风格,腾讯公司或其余公司创建API时要遵照的一种规则/风格,固然也有其余规则能够用。html
那么何为REST架构风格呢?首先咱们来讲说Web,由于rest是以web为平台的。咱们知道,web是分布式信息系统为超文本文件和其余对象(资源)提供访问入口。前端
在web上访问一个资源,须要3点:标识,表示,交互。经过这三个操做,又引出了三个概念:uri(包括url和urn),用来识别资源;representation(例如,图片,html,媒体)用来表示资源;经过协议与资源进行交互。因此,REST就是经过使用HTTP协议和URI,利用client/server对资源进行CRUD操做。python
那么为何要使用REST设计呢?确定是有它的优势的。git
1.客户端-服务端分离github
优势:提升用户界面的便携性,经过简化服务器提升可伸缩性....web
2..无状态(Stateless):从客户端的每一个请求要包含服务器所须要的全部信息django
优势:提升可见性(能够单独考虑每一个请求),提升了可靠性(更容易从局部故障中修复),提升可扩展性(下降了服务器资源使用)json
有状态与无状态区别:
如查询员工工资,若是查询工资是须要登录系统,进入查询工资的页面,执行相关操做,获取工资的多少,则这种状况下是有状态的,由于查询工资的每一步操做都依赖于前一步操做,只须要前面操做不成功,后序操做就没法执行;若是输入一个url便可获得指定员工的工资,则这种状况下是无状态的,由于获取员工工资不依赖于其余资源或者状态,且这种状况下,员工工资是一个资源,由一个url与其对应,能够经过HTTP的GET方法获得资源。
3.缓存(Cachable):服务器返回信息必须被标记是否能够缓存,若是缓存,客户端可能会重用以前的信息发送请求api
优势:减小交互次数,减小交互的平均延迟跨域
4.统一接口
优势:提升交互的可见性,鼓励单独改善组件
5.支持按需代码(Code-On-Demand 可选)
优势:提升可扩展性
下面来解释一下何为表征状态转移:
举个例子:例如我订阅了一我的的博客,想要获取他发表的全部文章(这里『他发表的全部文章』就是一个资源Resource)。因而我就向他的服务发出请求,说『我要获取你发表的全部文章,最好是atom格式的』,这时候服务器向你返回了atom格式的文章列表第一页(这里『atom格式的文章列表』就是表征Representation)。
你看到了第一页的页尾,想要看第二页,这时候有趣的事情就来了。若是服务器记录了应用的状态(stateful),那么你只要向服务询问『我要看下一页』,那么服务器天然就会返回第二页。相似的,若是你当前在第二页,想服务器请求『我要看下一页』,那就会获得第三页。可是REST的服务器偏偏是无状态的(stateless),服务器并无保持你当前处于第几页,也就没法响应『下一页』这种具备状态性质的请求。所以客户端须要去维护当前应用的状态(application state),也就是『如何获取下一页资源』。固然,『下一页资源』的业务逻辑必然是由服务端来提供。服务器在文章列表的atom表征中加入一个URI超连接(hyper link),指向下一页文章列表对应的资源。客户端就可使用统一接口(Uniform Interface)的方式,从这个URI中获取到他想要的下一页文章列表资源。上面的『可以进入下一页』就是应用的状态(State)。服务器把『可以进入下一页』这个状态以atom表征形式传输(Transfer)给客户端就是表征状态传输(REpresentational State Transfer)这个概念。
REST是面向资源进行的,而资源是经过URI进行暴露的。
URI 的设计只要负责把资源经过合理方式暴露出来就能够了。对资源的操做与它无关,操做是经过 HTTP动词来体现,因此REST 经过 URI 暴露资源时,会强调不要在 URI 中出现动词。
例如:好比:左边是错误的设计,而右边是正确的
GET /rest/api/getDogs --> GET /rest/api/dogs 获取全部小狗狗 GET /rest/api/addDogs --> POST /rest/api/dogs 添加一个小狗狗 GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一个小狗狗 GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一个小狗狗
REST很好利用了HTTP自己就有的一些特征,如HTTP动词,HTTP状态码,HTTP报头等等。
更多参考:https://github.com/aisuhua/restful-api-design-references
注:上面的解释是看见比较好的博客copy下来的,
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
经过在url上传参的形式传递搜索条件
更多状态码请看 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
状态码是4xx时,应返回错误信息,error当作key。
{
error: “Invalid API key”
}
针对不一样操做,服务器向用户返回的结果应该符合如下规范。
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” }}
先简单建立一个Django项目而且使用pip install djangorestframwork 安装django rest-framwork
要想快速实现的话能够点击这个快速实例
在开始rest-framework说使用以前咱们先说一说序列化
开发咱们的Web API的第一件事是为咱们的Web API提供一种将代码片断实例序列化和反序列化为诸如json
之类的表示形式的方式。咱们能够经过声明与Django forms很是类似的序列化器(serializers)来实现。
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32) price = models.IntegerField() pub_date = models.DateField() publish = models.ForeignKey("Publish") authors = models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/', views.bookView.as_view()), ]
from django.shortcuts import render, HttpResponse from app01.models import * from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers # 为queryset,model对象作序列化===》至关于form组件使用 # 这个能够放在一个单独的.py文件中 class BookSerializers(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() # 上面的只是针对的一对一字段的,若果用在一对多字段上的时候就会输出关联那张表的 def __str__(self) # publish = serializers.CharField() # 这个表示的是实现关联表中publish表中的name字段,能够本身定制 publish = serializers.CharField(source="publish.name") # authors=serializers.CharField(source="authors.all") # 当出现多对多的时候上面的方式也不可行,上面会显示的是一个queryset对象 # 本身定制显示什么 authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp # 这个至关于modelform组件同样 class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' depth = 1 # 当遇到一对多字段的时候 publish = serializers.CharField(source='publish.name') # 多对多字段 authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp # Create your views here. class BookViewSet(APIView): def get(self, request, *args, **kwargs): # 序列化 # 方式一: # book_data = list(Book.objects.all().values('name', 'email')) # return HttpResponse(book_data) # 方式二: # from django.forms.models import model_to_dict # book_data = Book.objects.all() # temp = [] # for obj in book_data: # temp.append(model_to_dict(obj)) # return HttpResponse(temp) # 方式三: # from django.core import serializers # book_data = Book.objects.all() # ret = serializers.serialize("json", book_data) # print(type(ret)) # return HttpResponse(ret) # 方式四:序列组件 # 这里面和Django里面的form组件和modelform组件类似 # 这里记住要是使用浏览器访问的话这个必需要在setting中的INSTALLED_APPS注册rest_framework要不就会报错 # 最好在项目一开始的时候就在setting里面注册 book_data = Book.objects.all() # many=True 表示的queryset对象,反之many=False就表示为model对象 # 至关于form组件 # bs = BookSerializers(book_data, many=True) bs = BookModelSerializers(book_data, many=True) return Response(bs.data)
记住要把在setting里面注册restframework。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'rest_framework' ]
在app01/utils/serializers.py 写serializers代码(相似于form)
from rest_framework import serializers # 为queryset,model对象作序列化===》至关于form组件使用 class BookSerializers(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() # 上面的只是针对的一对一字段的,若果用在一对多字段上的时候就会输出关联那张表的 def __str__(self) # publish = serializers.CharField() # 这个表示的是实现关联表中publish表中的name字段,能够本身定制 publish = serializers.CharField(source="publish.name") # authors=serializers.CharField(source="authors.all") # 当出现多对多的时候上面的方式也不可行,上面会显示的是一个queryset对象 # 本身定制显示什么 authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp
在app01/utils/serializers.py 写ModelSerializer代码(相似于modelform)
from app01.models import * # 导入表 from rest_framework import serializers # 这个至关于modelform组件同样 class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' # 当遇到一对多字段的时候 publish = serializers.CharField(source='publish.name') # 多对多字段 authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp
当一个model有外键的时候,默认显示的是外键的id,此时要显示外键的全部值能够用下面,depth,会把外键的全部值显示出来,depth应该是整数,代表嵌套的层级数量。
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' # 上面表示显示全部字段也能够只显示咱们想要的 # fields = ('id',) ,表示只显示id # 要是不想显示哪一个字段就可使用 # exclude=('id',), 表示出了id其余的都显示 depth = 1
上面就是关于get请求获取全部的数据。
一样 ModelSerializer也支持全局钩子和局部钩子,和form组件同样局部钩子为validate_字段名(form组件为clean_字段名),
全局钩子为validate。
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/', views.BookViewSet.as_view(), name="book"), ]
在app01/utils/serializers.py 里面的代码
from rest_framework import serializers
from app01.models import *
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' publish = serializers.HyperlinkedIdentityField( view_name="detail_publish", lookup_field="publish_id", lookup_url_kwarg="pk", ) # 当遇到一对多字段的时候 # source='publish.name'这个后面是publish.name因此咱们前面传回来的应该是publish.name对应的名字 # # 若是是publish.pk 前面传过来的就是 publish.pk对应的id publish = serializers.CharField(source='publish.name') # 多对多字段 authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): temp.append(obj.name) return temp # 由于上面本身定制了publish原来的save不支持这样保存,因此下面要重写create方法 def create(self, validated_data): print(validated_data) # 若是上面定制的publish.name 改成 publish.pk下面这2行就不用写了 if not validated_data["publish"]["name"].isdigit(): publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first() validated_data['publish_id'] = publish_id['publish_id'] else: validated_data['publish_id']=validated_data["publish"]["name"] # 其实前端通常都会传数字不会传汉字, 因此能够向下面这样写 # validated_data['publish_id'] = validated_data["publish"]["name"] validated_data.pop('publish') authors = validated_data.pop('authors') book = Book.objects.create(**validated_data) book.authors.add(*authors) return book # def update(self, instance, validated_data): print('validated_data', validated_data) if not validated_data["publish"]["name"].isdigit(): publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first() validated_data['publish_id'] = publish_id['publish_id'] else: validated_data['publish_id']=validated_data["publish"]["name"] validated_data.pop('publish') authors = validated_data.pop('authors') instance.update(**validated_data) # 更新普通字段和一对多字段 nid = instance.values('id')[0]['id'] instance.first().authors.set(authors) # 更新多对多字段 book_data = Book.objects.filter(id=nid).first() # 使用下面的放法前面传进来的instance是obj对象,而这里传进来的queryset对象像下面出入同样 # book_obj = Book.objects.filter(pk=pk).first() # bs = BookModelSerializers(book_obj, data=request.data) # instance.authors.set(authors) # book_data = super().update(instance, validated_data return book_data
from django.shortcuts import render, HttpResponse from app01.models import * from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers # 把Serializers和ModelSerializers单独放在一个py文件中 from app01.utils.serilizer import * import json # Create your views here. class BookViewSet(APIView): def get(self, request, *args, **kwargs): book_data = Book.objects.all() # many=True 表示的queryset对象,反之many=False就表示为model对象 # 至关于form组件 # bs = BookSerializers(book_data, many=True) # 这个就是至关于modelform组件, bs = BookModelSerializers(book_data, many=True) return Response(bs.data) def post(self, request, *args, **kwargs): bs = BookModelSerializers(data=request.data) if bs.is_valid(): # 直接把这个传到后面就不须要作验证了,对于多对多关系提交自定制显示 bs.save(authors=request.data['authors']) return Response(bs.data) else: return Response(bs.errors) class BookDetailViewSet(APIView): def get(self, request, pk): book_obj = Book.objects.filter(pk=pk).first() bs = BookModelSerializers(book_obj, context={'request': request}) return Response(bs.data) def put(self, request, pk): book_obj = Book.objects.filter(pk=pk) # 对应serilizer里面的 # book_obj = Book.objects.filter(pk=pk).first() # 这里若是是一对多或者多对多的自定制的话就须要本身写update方法 bs = BookModelSerializers(book_obj, data=request.data) if bs.is_valid(): bs.save(authors=request.data['authors']) return Response(bs.data) else: return Response(bs.errors) def delete(self, request, pk): Book.objects.filter(pk=pk).delete() return Response()
class BookDetailViewSet(APIView):
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first() bs = BookModelSerializers(book_obj) return Response(bs.data) def put(self, request, pk): book_obj = Book.objects.filter(pk=pk) # 这里若是是一对多或者多对多的自定制的话就须要本身写update方法 bs = BookModelSerializers(book_obj, data=request.data) if bs.is_valid(authors=request.data['authors']):
bs.save()
return Response(bs.data)
else:
return Response(bs.errors)
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response()
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' publish = serializers.HyperlinkedIdentityField( view_name="detail_publish", lookup_field="publish_id", lookup_url_kwarg="pk", )
上面的黄色字段就是超连接的关键,
view_name表示的是该路由的别名获取到publish/(?P<pk>\d+)/
lookup_field 表示在这个序列化里面获取到pk的值(也就是一个字段publish_id)
lookup_url_kwarg 表示把上面获取到的pk值放到view_name的(?P<pk>\d+)里面
在CBV相应部分
class BookDetailViewSet(APIView): def get(self, request, pk): book_obj = Book.objects.filter(pk=pk).first() bs = BookModelSerializers(book_obj, context={'request': request}) return Response(bs.data)
主要是在后面添加黄色部位
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/$', views.BookViewSet.as_view(), name="book"), url(r'^books/(?P<pk>\d+)/$', views.BookDetailViewSet.as_view(), name='detail_book'), url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailViewSet.as_view(), name='detail_publish'), ]
from rest_framework import mixins from rest_framework import generics
from app01.utils.serializers import * class BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
经过使用mixin类,咱们使用更少的代码重写了这些视图,但咱们还能够再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,咱们可使用它来简化咱们的views.py
模块。
from rest_framework import mixins from rest_framework import generics
from app01.utils.serializers import * class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookModelSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshModelSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshModelSerializers
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/$', views.BookViewSet.as_view({"get": "list", "post": "create"}), name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }), name="book_detail"), ]
views.py
from rest_framework import viewsets from app01.models import *
from app01.utils.serializers import *
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() # BookModelSerializers和前面的同样没什么变化功能相似于modelform serializer_class = BookModelSerializers
沿用上面的
token:服务端动态生成的1串用来检验用户身份的字符串,能够放在header、cokies、url参数(安全性较差)、请求体(CSRF token);
token和session相似,不一样于 session的是token比较灵活,不只仅能够cokies里
url.py的代码
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.LoginView.as_view(), name="login"), url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), ]
app01里面的models.py的代码
from django.db import models # Create your models here. class UserInfo(models.Model): USER_TYPE = ( (1, '普通用户'), (2, 'VIP'), (3, 'SVIP') ) user_type = models.IntegerField(choices=USER_TYPE, default=1) username = models.CharField(max_length=32) password = models.CharField(max_length=64) def __str__(self): return self.username # 设置 one to one 1个用户不能在不一样设备上登陆 # 设置 Freikey 支持1个用户 在不一样设备上同时登陆 class UserToken(models.Model): user = models.OneToOneField(UserInfo, on_delete=models.CASCADE) token = models.CharField(max_length=64) def __str__(self): return self.token class Book(models.Model): title = models.CharField(max_length=32) price = models.IntegerField() pub_date = models.DateField() publish = models.ForeignKey("Publish") authors = models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
CBV视图里面的代码
from django.shortcuts import render from rest_framework import viewsets from rest_framework.views import APIView from app01 import models from django.http import JsonResponse from app01.utils.auth import CustomAuthentication from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers # Create your views here. def md5(user): import hashlib import time # 当前时间,至关于生成一个随机的字符串 ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime, encoding='utf-8')) return m.hexdigest() class LoginView(APIView): """60s内任何人都只能够登陆3次""" authentication_classes = [] permission_classes = [] 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) class StartAuthentication(): """ 局部使用认证功能的状况下,优先继承该类 """ authentication_classes = [CustomAuthentication] class AuthorModelView(StartAuthentication,viewsets.ModelViewSet): """无论有没有登录,均可以查看且60内只能看三次""" # 若是不使用继承类来实现局部认证就可使用下面状况 # authentication_classes = [CustomAuthentication] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers
app01/utils/auth.py 里面的代码
from rest_framework import authentication,exceptions from app01.models import UserToken class CustomAuthentication(authentication.BaseAuthentication): def authenticate(self, request): token = request.GET.get('token') token_obj = UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('验证失败') return (token_obj.user, token_obj.token) def authenticate_header(self, request): pass
上面的局部认证只要在相应的类里面继承(StartAuthentication)就好了
只要在settings.py配置
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["resdemo.service.auth.CustomAuthentication"] }
上面黄色部位就是你的token认证组件的位置(也就是上面的author.py里面的CustomAuthentication类
若是要某一个不须要认证就在其中添加
authentication_classes = [] #里面为空,表明不须要认证,这个就是优先执行本身的,不执行全局的,由于本身的为空因此就是不要认证。
如上所示咱们配置了全局模式当咱们有一个不须要认证的时候就直接在这里面加上 authentication_classes = []
使用RestAPI认证功能小结
一、建立2张表userinfo 和usertoken表
二、认证类的authenticate方法去请求头中获取token信息,而后去token表中查询token是否存在;
三、查询到token 是正经常使用户(返回 用户名)不然为匿名用户(raise异常终止认证、或者 return none进行下一个认证)
四、局部应用
方式1::哪一个CBV须要认证在类中定义authentication_classes =[CustomAuthentication ]
方式2:额外定义1个类,CBV多继承
方式3:全局配置使用认证功能,那个CBV不使用authentication_classes =[ ]
五、全局使用 在配置文件中配置 ,注意从新建立一个模块,把认证类放里面;
本身写认证方法总结:
一、建立认证类
二、authenticate()方法返回值(三种)
三、须要建立导入的类
from rest_framework import authentication,exceptions from app01.models import UserToken
添加权限
一、在app01/utils/permission.py的文件中的代码
#!/usr/bin/evn python #-*-coding:utf-8-*- from rest_framework import permissions class SVIPPermission(permissions.BasePermission): message = "不是SVIP不给查看" # 当没有权限的时候提示信息 def has_permission(self, request, view): if request.user.user_type != 3: return False return True class MyPermission(permissions.BasePermission): message = "普通用户不给查看" def has_permission(self, request, view): if request.user.user_type == 1: return False return True
二、在setting里面设置全局权限
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.TokenAuth"], "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"], }
若是某一个视图不想要权限的话就能够在该视图中加 permission_class = []
或者不执行全局的权限只执行本身想要的权限
三、views.py 添加权限
from django.shortcuts import render from rest_framework import viewsets from rest_framework.views import APIView from app01 import models from django.http import JsonResponse from app01.utils.auth import CustomAuthentication from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers from app01.utils.permission import MyPermission # Create your views here. def md5(user): import hashlib import time # 当前时间,至关于生成一个随机的字符串 ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime, encoding='utf-8')) return m.hexdigest() class LoginView(APIView): """60s内任何人都只能够登陆3次""" authentication_classes = [] permission_classes = [] 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) class StartAuthentication(): """ 局部使用认证功能的状况下,优先继承该类 """ authentication_classes = [CustomAuthentication] class AuthorModelView(viewsets.ModelViewSet): """无论有没有登录,均可以查看且""" # 不执行认证和权限认证 authentication_classes = [] permission_classes = [] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers class BookModelView(viewsets.ModelViewSet): """登录后SVIP用户能够查看""" # 执行全局的权限和认证,全局的权限是只能SVIP用户能够访问 queryset = models.Book.objects.all() serializer_class = BookModelSerializers class PublisherModelView(viewsets.ModelViewSet): """登录后普通用户不能够看""" # 不执行全局的权限,只执行MyPermission这个权限 permission_classes = [MyPermission] queryset = models.Publish.objects.all() serializer_class = PublishModelSerializers
四、url.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.LoginView.as_view(), name="login"), url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), ]
五、app01/utils/auth.py的代码上面有不须要改变的,这里就不写了
总结:
(1)使用
(2)返回值
(3)局部
(4)全局
REST_FRAMEWORK = { #权限 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPremission'], }
from rest_framework import throttling import time VISIT_RECORD = {} class VisitThrottle(throttling.BaseThrottle): """60s内只能访问三次""" def __init__(self): self.history = None # 初始化访问记录 def allow_request(self, request, view): remote_addr = self.get_ident(request) ctime = time.time() # 若是当前IP不在访问记录里面,就添加到记录 if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime, ] # 键值对的形式保存 return True # True表示能够访问 # 获取当前ip的历史访问记录 history = VISIT_RECORD.get(remote_addr) self.history = history # 若是有历史访问记录,而且最先一次的访问记录离当前时间超过60s,就删除最先的那个访问记录, # 只要为True,就一直循环删除最先的一次访问记录 # 结合下面的3次能够知道当咱们事件 while history and history[-1] < ctime - 60: # 最先的访问一次加60s小于当前时间 history.pop() # 若是访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个) if len(history) < 3: history.insert(0, ctime) return True else: return False def wait(self): '''还须要等多久才能访问''' ctime = time.time() return 60 - (ctime - self.history[-1])
from django.shortcuts import render from rest_framework import viewsets from rest_framework.views import APIView from app01 import models from django.http import JsonResponse from app01.utils.auth import CustomAuthentication from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers from app01.utils.permission import MyPermission # Create your views here. def md5(user): import hashlib import time # 当前时间,至关于生成一个随机的字符串 ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime, encoding='utf-8')) return m.hexdigest() class LoginView(APIView): """60s内任何人都只能够登陆3次""" authentication_classes = [] permission_classes = [] 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) class StartAuthentication(): """ 局部使用认证功能的状况下,优先继承该类 """ authentication_classes = [CustomAuthentication] class AuthorModelView(viewsets.ModelViewSet): """无论有没有登录,均可以查看且60内只能看三次""" # 不执行认证和权限认证 authentication_classes = [] permission_classes = [] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers class BookModelView(viewsets.ModelViewSet): """登录后SVIP用户能够查看不限次数""" # 执行全局的权限和认证,全局的权限是只能SVIP用户能够访问 throttle_classes = [] queryset = models.Book.objects.all() serializer_class = BookModelSerializers class PublisherModelView(viewsets.ModelViewSet): """登录后普通用户不能够看且60内只能看三次""" # 不执行全局的权限,只执行MyPermission这个权限 permission_classes = [MyPermission] queryset = models.Publish.objects.all() serializer_class = PublishModelSerializers
REST_FRAMEWORK = { #节流 "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.VisitThrottle'], }
(1)throttle.py
from rest_framework import throttling class VisitThrottle2(throttling.SimpleRateThrottle): '''匿名用户60s只能访问三次(根据ip)''' scope = 'NBA' # 这里面的值,本身随便定义,settings里面根据这个值配置Rate def get_cache_key(self, request, view): # 经过ip限制节流 return self.get_ident(request) class UserThrottle(throttling.SimpleRateThrottle): '''登陆用户60s能够访问10次''' scope = 'NBAUser' # 这里面的值,本身随便定义,settings里面根据这个值配置Rate def get_cache_key(self, request, view): return request.user.username
(2)settings.py
#全局 REST_FRAMEWORK = { #节流 "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.UserThrottle'], #全局配置,登陆用户节流限制(10/m) "DEFAULT_THROTTLE_RATES":{ 'NBA':'3/m', #没登陆用户3/m,NBA就是scope定义的值 'NBAUser':'10/m', #登陆用户10/m,NBAUser就是scope定义的值 } }
(3) views.py
from django.shortcuts import render from rest_framework import viewsets from rest_framework.views import APIView from app01 import models from django.http import JsonResponse from app01.utils.auth import CustomAuthentication from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers from app01.utils.permission import MyPermission from app01.utils.throttle import VisitThrottle2 # Create your views here. def md5(user): import hashlib import time # 当前时间,至关于生成一个随机的字符串 ctime = str(time.time()) m = hashlib.md5(bytes(user, encoding='utf-8')) m.update(bytes(ctime, encoding='utf-8')) return m.hexdigest() class LoginView(APIView): """60s内任何人都只能够登陆3次""" authentication_classes = [] permission_classes = [] 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) class StartAuthentication(): """ 局部使用认证功能的状况下,优先继承该类 """ authentication_classes = [CustomAuthentication] class AuthorModelView(viewsets.ModelViewSet): """无论有没有登录,均可以查看且60内只能看三次使用的是自定制的节流""" # 不执行认证和权限认证 authentication_classes = [] permission_classes = [] # 由于全局设置的是登陆用户(10/m),这里不须要登陆,因此就是用匿名用户ip限流(3/m) throttle_classes = [VisitThrottle2] queryset = models.Author.objects.all() serializer_class = AuthorModelSerializers class BookModelView(viewsets.ModelViewSet): """登录后SVIP用户能够查看不限次数""" # 执行全局的权限和认证,全局的权限是只能SVIP用户能够访问 throttle_classes = [] queryset = models.Book.objects.all() serializer_class = BookModelSerializers class PublisherModelView(viewsets.ModelViewSet): """登录后普通用户不能够看,且SVIP用户60s内能看10次使用的是内置的节流""" # 不执行全局的权限,只执行MyPermission这个权限 permission_classes = [MyPermission] queryset = models.Publish.objects.all() serializer_class = PublishModelSerializers
(4) 里面的认证和权限和上面的同样
说明:
from django.db import models # Create your models here. class UserInfo(models.Model): USER_TYPE = ( (1, '普通用户'), (2, 'VIP'), (3, 'SVIP') ) user_type = models.IntegerField(choices=USER_TYPE, default=1) username = models.CharField(max_length=32) password = models.CharField(max_length=64) def __str__(self): return self.username # 设置 one to one 1个用户不能在不一样设备上登陆 # 设置 Freikey 支持1个用户 在不一样设备上同时登陆 class UserToken(models.Model): user = models.OneToOneField(UserInfo, on_delete=models.CASCADE) token = models.CharField(max_length=64) def __str__(self): return self.token class Book(models.Model): title = models.CharField(max_length=32) price = models.IntegerField() pub_date = models.DateField() publish = models.ForeignKey("Publish") authors = models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.CustomAuthentication"], "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"], "DEFAULT_THROTTLE_CLASSES": ['app01.utils.throttle.UserThrottle'], # 全局配置,登陆用户节流限制(10/m) "DEFAULT_THROTTLE_RATES": { 'NBA': '4/m', # 没登陆用户4/m(表示60s4次),NBA就是scope定义的值 'NBAUser': '10/m', # 登陆用户10/m,NBAUser就是scope定义的值 } }
#!/usr/bin/evn python #-*-coding:utf-8-*- from rest_framework import permissions class SVIPPermission(permissions.BasePermission): message = "不是SVIP不给查看" # 当没有权限的时候提示信息 def has_permission(self, request, view): if request.user.user_type != 3: return False return True class MyPermission(permissions.BasePermission): message = "普通用户不给查看" def has_permission(self, request, view): if request.user.user_type == 1: return False return True
#!/usr/bin/evn python # -*-coding:utf-8-*- from rest_framework import authentication,exceptions from app01.models import UserToken class CustomAuthentication(authentication.BaseAuthentication): def authenticate(self, request): token = request.GET.get('token') token_obj = UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('验证失败') return (token_obj.user, token_obj.token) def authenticate_header(self, request): pass
#!/usr/bin/evn python #-*-coding:utf-8-*- from app01.models import * from rest_framework import serializers class AuthorModelSerializers(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" class PublishModelSerializers(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__"
#!/usr/bin/evn python # -*-coding:utf-8-*- from rest_framework import throttling import time VISIT_RECORD = {} class VisitThrottle(throttling.BaseThrottle): """60s内只能访问三次""" def __init__(self): self.history = None # 初始化访问记录 def allow_request(self, request, view): remote_addr = self.get_ident(request) ctime = time.time() # 若是当前IP不在访问记录里面,就添加到记录 if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime, ] # 键值对的形式保存 return True # True表示能够访问 # 获取当前ip的历史访问记录 history = VISIT_RECORD.get(remote_addr) self.history = history # 若是有历史访问记录,而且最先一次的访问记录离当前时间超过60s,就删除最先的那个访问记录, # 只要为True,就一直循环删除最先的一次访问记录 # 结合下面的3次能够知道当咱们事件 while history and history[-1] < ctime - 60: # 最先的访问一次加60s小于当前时间 history.pop() # 若是访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个) if len(history) < 3: history.insert(0, ctime) return True else: return False def wait(self): '''还须要等多久才能访问''' ctime = time.time() return 60 - (ctime - self.history[-1]) class VisitThrottle2(throttling.SimpleRateThrottle): '''匿名用户60s只能访问4次(根据ip)''' scope = 'NBA' # 这里面的值,本身随便定义,settings里面根据这个值配置Rate def get_cache_key(self, request, view): # 经过ip限制节流 return self.get_ident(request) class UserThrottle(throttling.SimpleRateThrottle): '''登陆用户60s能够访问10次''' scope = 'NBAUser' # 这里面的值,本身随便定义,settings里面根据这个值配置Rate def get_cache_key(self, request, view): return request.user.username
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.LoginView.as_view(), name="login"), url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), ]