1 什么是drf?
在python项目开发中,前后端分离的技术框架越来越成熟,在前后端进行通信时,通常需要用统一的格式进行通信,目前应用比较广泛的是RESTful API。那后端如何快速编写基于Django的RESTful API呢?本篇将主要介绍使用DjangoRestFramework(drf)框架来快速开发符合REST风格的API。
2 为什么用drf?
提供了可视化的API调试界面,开发者可以在线测试接口
可以根据需求来选择常规视图功能或更高级的功能
不用自己写大量的CRUD接口了,简单配置即可
支持ORM(对象映射关系)和非ORM的数据序列化
# 安装drfpip install djangorestframework# 具体功能在具体模块下from rest_framework.request import Requestfrom rest_framework.response import Responsefrom rest_framework.exceptions import APIExceptionfrom rest_framework.filters import OrderingFilterfrom rest_framework.views import APIViewfrom rest_framework.pagination import PageNumberPaginationfrom rest_framework.settings import APISettings# 注册drfappINSTALLED_APPS = [ ... 'rest_framework',]复制代码
as_view: 就干了一件事,禁用csrf认证
三大认证任务分析
**认证模块:**校验用户是是否登陆
self.perform_authentication(request)复制代码
**权限模块:**校验用户是否拥有权限
self.check_permissionsn(request)复制代码
**节流模块:**访问接口的次数在设定的时间范围内是否过快(配置访问频率、缓存计次、超次后需要等待的时间)
self.check_throttles(request)复制代码
3 Django请求生命周期
前端发送请求
wsgi, 他就是socket服务端,用于接收用户请求并将请求进行初次封装,然后将请求交给web框架(Flask、Django)
中间件处理请求,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session
路由匹配,根据当前请求的URL找到视图函数,如果是FBV写法,通过判断method两类型,找到对应的视图函数;如果是CBV写法,匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行
视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm、view视图将数据渲染到template模板
视图函数执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端
中间件处理响应
wsgi,将响应的内容发送给浏览器
浏览器渲染
4 drf请求生命周期
前端发送请求 --> Django的wsgi --> 中间件 --> 路由系统_执行CBV的as_view(),就是执行内部的dispath方法 --> 在执行dispath之前,有版本分析和渲染器 --> 在dispath内,对request封装 --> 版本 --> 认证 --> 权限 --> 限流 --> 通过反射执行视图函数 --> 如果视图用到缓存( request.data or request.query_params )就用到了 解析器 --> 视图处理数据,用到了序列化(对数据进行序列化或验证) --> 视图返回数据可以用到分页
认证流程
Django认证与三种方法,此处使用第三种自定义方法
方法一 系统session认证
rest_framework.authentication.SessionAuthenticationajax请求通过认证:cookie中要携带 sessionid、csrftoken,请求头中要携带 x-csrftoken复制代码
方法二 jwt认证
rest_framework_jwt.authentication.JSONWebTokenAuthenticationajax请求通过认证:请求头中要携带 authorization,值为 jwt空格token复制代码
方法三(常用)自定义:基于jwt、其它
1)自定义认证类,继承BaseAuthentication(或其子类),重写authenticate2)authenticate中完成 拿到认证标识 auth 反解析出用户 user 前两步操作失败 返回None => 游客 前两步操作成功 返回user,auth => 登录用户 注:如果在某个分支抛出异常,直接定义失败 => 非法用户复制代码
1 流程
1.1 源码内容
当用户进行登录的时候,运行了登录类的as_view()方法,进入了APIView类的dispatch方法
执行self.initialize_request这个方法,里面封装了request和认证对象列表等其他参数
执行self.initial方法中的self.perform_authentication,里面运行了user方法
再执行了user方法里面的self._authenticate()方法
1.2 自定义方法
然后执行了自己定义的类中的authenticate方法,自己定义的类继承了BaseAuthentication类,里面有authenticate方法,如果自己定义的类中没有authenticate方法会报错
把从authenticate方法得到的user和auth赋值给user和auth方法
这两个方法把user和auth的值分别赋值给request.user:是登录用户的对象;request.auth:是认证的信息字典
2 返回值
没有携带认证信息,直接返回 None => 游客
有认证信息,校验成功,返回一个元组,第一个参数赋值给request.user,第二个赋值给request.auth
有认证信息,校验失败,抛异常 => 非法用户
3 源码介绍
# APIView的dispatch中有个self.initialize_request,它返回了一个Request类,
它封装了django的request和认证对象列表等其他参数
class APIView(View):
# 1.进入了APIView类的dispatch方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 2.执行了self.initialize_request这个方法,封装了request和认证对象列表等其他参数
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 3.执行self.initial方法中的self.perform_authentication,里面运行了user方法
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)
# 2.执行了self.initialize_request这个方法,封装了request和认证对象列表等其他参数
'''
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # [MyAuthentication(),]
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
'''
# 3.执行self.initial方法中的self.perform_authentication,里面运行了user方法
'''
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# 版本方法在这里
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# 认证 权限 节流三兄弟
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
'''
# 3.执行self.initial方法中的self.perform_authentication,里面运行了user方法
def perform_authentication(self, request):
request.user
# 4、再执行了user方法里面的self._authenticate()方法
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
4 使用
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api import models
class Authtication(BaseAuthentication):
# 常用
def authenticate(self,request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).fitst()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在rest framework内部将会将整个两个字段赋值给request,以供后续操作使用
return (token_obj.user, token_obj)
# 这个方法一般用不到
def authenticate_header(self, request):
pass
5 引入
5.1 局部使用
from rest_framework.views import APIView
from apps.api.auth import Authtication
class UserInfoView(APIView):
# 单独只对这个视图认证
authentication_classes = [Authtication,]
def get(self, request, *args, **kwargs):
print(request.user)
return HttpResponse('用户相关信息')
5.2 全局使用setting.py
REST_FRAMEWORK = {
# 全局配置
# 'DEFAULT_AUTHENTICATION_CLASSES': ['apps.api.utils.auth.FirstAuthtication', 'apps.api.utils.auth.Authtication'],
'DEFAULT_AUTHENTICATION_CLASSES': ['apps.api.utils.auth.Authtication'],
# 匿名,request.user = None
'UNAUTHENTICATED_USER': None,
# 匿名,request.auth = None
'UNAUTHENTICATED_TOKEN': None,
}
鉴权流程
方法一:
1)AllowAny:允许所有用户,校验方法直接返回True
2)IsAuthenticated:只允许登录用户
必须request.user和request.user.is_authenticated都通过
3)IsAuthenticatedOrReadOnly:游客只读,登录用户无限制
get、option、head 请求无限制
前台请求必须校验 request.user和request.user.is_authenticated
4)IsAdminUser:是否是后台用户
校验 request.user和request.user.is_staff is_staff(可以登录后台管理系统的用户)
方法二:
自定义:基于auth的Group与Permission表
1)自定义权限类,继承BasePermission,重写has_permission
2)has_permission中完成
拿到登录用户 user <= request.user
校验user的分组或是权限
前两步操作失败 返回False => 无权限
前两步操作成功 返回True => 有权限
1 流程
1.1 源码内容
当用户执行一个业务的时候,运行了as_view方法,进入了APIView类的dispatch方法
此处不需要封装request了,因为在认证过程中封装了
进入self.initial方法中的self.check_permissions(request)方法
里面执行了for循环,把每个权限类实例化对象
1.2 自定义方法
执行自己定义的权限类里面的has_permission方法,里面会判断request.user是否存在
不存在就返回False,存在就返回True
之后执行self.permission_denied报错方法,返回的是False就报错,可以自定义报错信息,在has_permission方法中写message = {"status": False, "error": "登录成功之后才能评论"},就实现了自定义报错
如果返回的是True就让他进入功能
2 返回值
True, 有权限,进行下一步认证(频率认证)
False, 无权限,将信息返回给前台
3 使用
重写原生方法BasePermission.has_permission
必须继承BasePermission,必须实现has_permission
utils/permission.py
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
message = '必须是管理员才能访问'
def has_permission(self, request, view):
if request.user.user_type != 1:
return False
return True
models.py
from django.db import models
class UserInfo(models.Model):
user_type_choices = [
(1, '普通用户'),
(2, 'VIP用户'),
(3, 'SVIP用户'),
]
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
user_type = models.IntegerField(choices=user_type_choices, default=0)
4 引入
4.1 全局
# settings.py
'DEFAULT_PERMISSION_CLASSES': ['apps.api.utils.permisson.MyPermission']
4.2 局部
class UserInfoView(APIView):
"""
用户信息
"""
permission_classes = [MyPermisson, ]
def get(self, request, *args, **kwargs):
print(request.user)
return HttpResponse('用户相关信息')
网友评论