认证
- 身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制。然后 权限 和 限制 组件决定是否拒绝这个请求。
- 认证确定了你是谁
- 权限确定你能不能访问某个接口
- 限制确定你访问某个接口的频率

一、自定义Token认证
REST framework 提供了一些身份验证方案,并且还允许实现自定义方案。
1、表:
class UserInfo(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)
type = models.SmallIntegerField(
choices=((1, '普通用户'), (2, 'VIP用户')),
default=1
)
class Token(models.Model):
user = models.OneToOneField(to='UserInfo')
token_code = models.CharField(max_length=128)
2、定义一个登录视图:
def get_random_token(username):
"""
根据用户名和时间戳生成随机token
:param username:
:return:
"""
import hashlib, time
timestamp = str(time.time())
m = hashlib.md5(bytes(username, encoding="utf8"))
m.update(bytes(timestamp, encoding="utf8"))
return m.hexdigest()
class LoginView(APIView):
"""
校验用户名密码是否正确从而生成token的视图
"""
def post(self, request):
res = {"code": 0}
print(request.data)
username = request.data.get("username")
password = request.data.get("password")
user = models.UserInfo.objects.filter(username=username, password=password).first()
if user:
# 如果用户名密码正确
token = get_random_token(username)
models.Token.objects.update_or_create(defaults={"token_code": token}, user=user)
res["token"] = token
else:
res["code"] = 1
res["error"] = "用户名或密码错误"
return Response(res)
3、定义一个认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class MyAuth(BaseAuthentication):
def authenticate(self, request):
if request.method in ["POST", "PUT", "DELETE"]:
request_token = request.data.get("token", None)
if not request_token:
raise AuthenticationFailed('缺少token')
token_obj = models.Token.objects.filter(token_code=request_token).first()
if not token_obj:
raise AuthenticationFailed('无效的token')
return token_obj.user.username, None
else:
return None, None
4、视图级别认证
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
authentication_classes = [MyAuth, ]
5、全局级别认证
# 在settings.py中配置
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ]
}
权限
只有VIP用户才能看的内容。
一、自定义
1、自定义一个权限类
# 自定义权限
class MyPermission(BasePermission):
message = 'VIP用户才能访问'
def has_permission(self, request, view):
"""
自定义权限只有VIP用户才能访问
"""
# 因为在进行权限判断之前已经做了认证判断,所以这里可以直接拿到request.user
if request.user and request.user.type == 2: # 如果是VIP用户
return True
else:
return False
2、视图级别配置
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
authentication_classes = [MyAuth, ]
permission_classes = [MyPermission, ]
3、全局级别设置
# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
"DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
}
限制
DRF内置了基本的限制类,首先我们自己动手写一个限制类,熟悉下限制组件的执行过程。
一、自定义
1、自定义限制类
VISIT_RECORD = {}
# 自定义限制
class MyThrottle(object):
def __init__(self):
self.history = None
def allow_request(self, request, view):
"""
自定义频率限制60秒内只能访问三次
"""
# 获取用户IP
ip = request.META.get("REMOTE_ADDR")
timestamp = time.time()
if ip not in VISIT_RECORD:
VISIT_RECORD[ip] = [timestamp, ]
return True
history = VISIT_RECORD[ip]
self.history = history
history.insert(0, timestamp)
while history and history[-1] < timestamp - 60:
history.pop()
if len(history) > 3:
return False
else:
return True
def wait(self):
"""
限制时间还剩多少
"""
timestamp = time.time()
return 60 - (timestamp - self.history[-1])
2、视图使用
class CommentViewSet(ModelViewSet):
queryset = models.Comment.objects.all()
serializer_class = app01_serializers.CommentSerializer
throttle_classes = [MyThrottle, ]
3、全局使用
# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
"DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
"DEFAULT_THROTTLE_CLASSES": ["app01.utils.MyThrottle", ]
}
一、内置
1、使用内置限制类
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "xxx"
def get_cache_key(self, request, view):
return self.get_ident(request)
2、全局配置
# 在settings.py中设置rest framework相关配置项
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.MyAuth", ],
# "DEFAULT_PERMISSION_CLASSES": ["app01.utils.MyPermission", ]
"DEFAULT_THROTTLE_CLASSES": ["app01.utils.VisitThrottle", ],
"DEFAULT_THROTTLE_RATES": {
"xxx": "5/m",
}
}
DRF内置分页器
- rest framework中提供了三种分页模式:
- from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
一、PageNumberPagination
- 按页码数分页,第n页,每页显示m条数据
- 例如:http://127.0.0.1:8000/api/article/?page=2&size=1
1、分页器
class MyPageNumber(PageNumberPagination):
page_size = 2 # 每页显示多少条
page_size_query_param = 'size' # URL中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
max_page_size = None # 最大页码数限制
2、视图
class ArticleList(APIView):
def get(self, request, *args, **kwargs):
res = {"code": 0}
article_list = models.Article.objects.all().order_by("id")
# 分页
page_obj = MyPageNumber()
page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
ser_obj = ArticleSerializer(page_article, many=True)
res["data"] = ser_obj.data
return Response(res)
3、返回带页码链接的响应
class ArticleList(APIView):
def get(self, request, *args, **kwargs):
res = {"code": 0}
article_list = models.Article.objects.all().order_by("id")
# 分页
page_obj = MyPageNumber()
page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
ser_obj = ArticleSerializer(page_article, many=True)
res["data"] = ser_obj.data
return page_obj.get_paginated_response(res)
二、LimitOffsetPagination
- 分页,在n位置,向后查看m条数据
- 例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2
1、分页器
# offset分页
class MyLimitOffset(LimitOffsetPagination):
default_limit = 1
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 999
2、视图
class ArticleList(APIView):
def get(self, request, *args, **kwargs):
res = {"code": 0}
article_list = models.Article.objects.all().order_by("id")
# 分页
page_obj = MyLimitOffset()
page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
ser_obj = ArticleSerializer(page_article, many=True)
res["data"] = ser_obj.data
return page_obj.get_paginated_response(res)
三、CursorPagination
- 加密分页,把上一页和下一页的id值记住
1、分页器
# 加密分页
class MyCursorPagination(CursorPagination):
cursor_query_param = 'cursor'
page_size = 1
ordering = '-id' # 重写要排序的字段
2、视图
class ArticleList(APIView):
def get(self, request, *args, **kwargs):
res = {"code": 0}
article_list = models.Article.objects.all().order_by("id")
# 分页
page_obj = MyCursorPagination()
page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self)
ser_obj = ArticleSerializer(page_article, many=True)
res["data"] = ser_obj.data
# return Response(res)
return page_obj.get_paginated_response(res)
四、全局配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
五、局部配置
可以在视图类中进行局部设置
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer pagination_class = PageNumberPagination # 注意不是列表(只能有一个分页模式)
解析器
- 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。
- 在了解解析器之前,要先知道Accept以及ContentType请求头。
- Accept是告诉对方能解析什么样的数据,通常也可以表示想要什么样的数据。
- 解析器工作原理的就是拿到请求的ContentType来判断前端给后端的数据类型是什么,然后在后端使用相应的解析器去解析数据。
一、Django中的数据解析
- 在视图中可以通过request.POST来获取前端发来的请求数据,那么Django框架是如何拿到请求体中的数据的呢?
- 首先,request对象是 WSGIRequest 类的实例化对象
- 在Django的视图中通过request.POST和request.FILES能够取到数据都是因为在这里把请求的数据解析,并赋值给request对象了。
- Django的解析器是不支持 ContenType为 application/json 的,也就是说无法解析json格式的数据。


二、DRF中的解析器
- DRF中获取请求提交的数据是通过访问request.data
- 如果没有配置解析器,DRF会使用默认的解析器:
- 可以在单个视图或者全局的settings.py中配置要使用的解析器。
- 注意:当项目中只配置了 JSONParser 解析器时,就只能解析JSON格式的数据了,客户端如果使用浏览器提交,将无法解析。
- 注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。





1、单个视图配置
class BookViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
parser_classes = [JSONParser, ]
2、全局配置
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
)
}
渲染器
- 渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。
- 可以在视图中局部设置也可以在全局的settings.py中进行设置
- 注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。
- DRF提供的渲染器有很多,默认是:
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
1、局部设置
这样设置后就只能返回JSON格式的数据了,并不会像之前一样提供一个阅读友好的web页面。
class PublisherViewSet(ModelViewSet):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
renderer_classes = [JSONRenderer, ]
2、全局设置
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
}
DRF的版本控制
- API 版本控制允许在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。 DRF提供了许多不同的版本控制方案。
- 可能会有一些客户端因为某些原因不再维护了,但是后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。
一、DRF提供的版本控制方案
DRF提供了五种版本控制方案,如下图:

二、版本控制系统的使用
1、全局配置
这里以URLPathVersioning 为例,还是在项目的settings.py中REST_FRAMEWORK配置项下配置:
REST_FRAMEWORK = {
...
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1', # 默认的版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 有效的版本
'VERSION_PARAM': 'version', # 版本的参数名与URL conf中一致
}
2、urls.py中
urlpatterns = [
...
url(r'^(?P<version>[v1|v2]+)/publishers/$', views.PublisherViewSet.as_view({'get': 'list', 'post': 'create'})),
url(r'^(?P<version>[v1|v2]+)/publishers/(?P<pk>\d+)/$', views.PublisherViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
3、视图中
- 视图中可以通过访问 request.version 来获取当前请求的具体版本,然后根据不同的版本来返回不同的内容。
- 可以在视图中自定义具体的行为,下面以不同的版本返回不同的序列化类为例。
class PublisherViewSet(ModelViewSet):
def get_serializer_class(self):
"""不同的版本使用不同的序列化类"""
if self.request.version == 'v1':
return PublisherModelSerializerVersion1
else:
return PublisherModelSerializer
queryset = models.Publisher.objects.all()
4、局部配置
注意,通常是不会单独给某个视图设置版本控制的,如果你确实需要给单独的视图设置版本控制,可以在视图中设置versioning_class属性,如下:
class PublisherViewSet(ModelViewSet):
...
versioning_class = URLPathVersioning
来源:https://www.cnblogs.com/bubu99/p/11112008.html