DRF 请求生命周期以及各模块解析
rest_framework框架的封装特点
# 导入 rest_framework
# rest_framework的导入风格就是按照英文的意思就可以了。
import rest_framework
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.exceptions import APIException
from rest_framework.pagination import PageNumberPagination
from rest_framework.settings import APISettings
from rest_framework.parsers import JSONParser
from rest_framework.filters import OrderingFilter
原生Django与DRF比较
原生Django
from django.views import View
from django.http import JsonResponse
class BookView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({
'msg': 'view get ok'
})
def post(self, request, *args, **kwargs):
return JsonResponse({
'msg': 'view post ok'
})
drf
from rest_framework.views import APIView
from rest_framework.response import Response
class BookAPIView(APIView):
def get(self, request, *args, **kwargs):
return Response({
'msg': 'apiview get ok'
})
def post(self, request, *args, **kwargs):
return Response({
'msg': 'apiview post ok'
})
django的配置文件中的CSRF认证注释去掉。
测试原生的Django接口和DRF接口发现:
原生Django接口提交post请求会被django拒绝
用DRF写的接口不会被拒
我们只有看了源码才知道,是因为DRF接口继承了APIView,内部重写了原生View类,并添加了新功能:局部禁用csrf
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
# 局部禁用csrf
return csrf_exempt(view)
APIView 的请求生命周期
-
APIView 类继承View类,重写了as_view和dispatch方法
-
APIView类重写的as_view()方法本质上还是View的as_view,只是在返回视图view函数地址时,局部禁用了csrf认证
-
APIView中重写dispatch方法:
- 在执行请求逻辑前,执行请求模块(二次封装request对象),解析模块(三种数据包格式的数据解析)
- 在执行请求逻辑中,执行逻辑,若出现异常交给异常模块处理
- 在执行请求逻辑后,执行响应模块(二次封装response)、渲染模块(响应的数据能够使用JSON和页面两种方式渲染)
请求模块(request)
-
将wsgi的request对象二次封装,转换成drf的Request类的对象
-
drf封装后的request对象完全兼容wsgi的request对象,并且保存原request对象在新request._request方法中:
new_request.__dict__ = request.__dict new_request._request = request
-
重新格式化请求数据存放位置
拼接参数:request.query_params
数据包参数:request.data
源码分析:
1. 入口:APIVIew的dispatch方法的 request=self.initialize_request(request, *args, **kwargs)
2. print(request._request.method) # 在内部将wsgi的request赋值给request._request
3. print(request.method) # 就是通过__getattr__走的是request._request.method
4. print(request.query_params) # 走的是方法属性,就是给request._request.GET重新命名
5. print(request.data) # 走的是方法属性,值依赖于request._full_data
解析模块(parser_classes)
只处理请求的数据包参数(request.data)
- form-data
- urlencoded
- application/json
可以针对全局配置、局部配置 方式
全局配置,在项目settings配置文件中配置
# drf框架自定义配置,不需要哪个把哪个注释掉就可以了。
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
}
局部配置,在视图类中实现:
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
class BookAPIView(APIView):
parser_classes = [JSONParser, FormParser, MultiPartParser]
配置好之后解析模块的查找顺序:
局部(视图类中)->全局(settings配置文件中)->drf默认配置(APIView中)
源码分析:
1. 入口:APIVIew的dispatch方法的 request=self.initialize_request(request, *args, **kwargs)
2. 获取解析类:parsers=self.get_parsers(),
3. 进行局部全局默认配置查找顺序进行查找:return [parser() for parser in self.parser_classes]
源码:
class APIView(View):
# The following policies may be set at either globally, or per-view.
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]
异常模块(exception_handler)
异常模块就是在客户端或者服务端请求的时候出现异常了采取的措施:
我们可以重写捕获异常的方法:
源码分析:
1. 入口:APIVIew的dispatch方法:
except Exception as exc: # 遇到异常捕获异常
response = self.handle_exception(exc)
2. 执行handle_exception方法:
exception_handler = self.get_exception_handler()
得到django中提供的异常模块处理方法;
context = self.get_exception_handler_context()
生成一个大字典,包含类名称,请求参数,不变长参数等等
response = exception_handler(exc, context)
执行函数,传入大字典响应给客户端
这里我们看了源码知道了django中的异常捕获是通过一个函数方法实现,我们也可以自定义我们的异常处理信息:
在settings中配置:
REST_FRAMEWORK = {
# 异常模块处理
# 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', django中提供的
'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
在api的新建一个exception py文件,文件内写入:
# 一定要在settings文件中将异常模块配置自己的异常处理函数
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
detail = '%s - %s - %s' % (context.get('view'), context.get('request').method, exc)
if not response: # 服务端错误
response = Response({'detail': detail})
else:
response.data = {'detail': detail}
# 核心:要将response.data.get('detail')信息记录到日志文件
# logger.waring(response.data.get('detail'))
return response
通过我们自定义的一个异常处理方法最终其他人在用我们的接口的时候,如果接口错误会返回以下信息,而不会返回一些乱七八糟的页面了。
{
"detail": "<api.views.CarAPIView object at 0x000000000D2C6320> - PUT - 方法 “PUT” 不被允许。"
}
响应模块(Response)
Response,可以对响应的数据指定参数:
- data:响应的数据(data=)
- status:网络的状态码(status=)
- template_name:drf前后台不分离的情况下返回的页面
- headers:响应头,一般不写,走默认的
- exception:异常响应,默认为False
- content_type:默认是application/json的数据格式
from rest_framework.views import APIView
from rest_framework.response import Response
class CarAPIView(APIView):
def get(self,request,*args,**kwargs):
print(request.method)
print(request._request.method)
return Response(data={"msg":"apiview get ok"},status=200)
渲染模块(render)
可以做全局配置或局部配置,主要针对客户端返回的数据格式做限制
postman测试工具返回的是json,浏览器请求到的结果是一个页面
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
# 局部渲染
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
# 全局渲染配置
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer', # 项目真正上线的时候会把它注释掉,因为浏览器访问接口是一个页面
],
}
二次封装Response类
(自定义一个APIResponse类继承Response),优化响应
正确的:APIResponse('results'=[]) # 数据状态码和状态信息有默认值,可以不传
异常的:Response(1,'error', status=400) # 可以按位传数据状态码和状态信息,错误时还可以设置网络状态码
封装后的响应与封装前的响应结果一致,但是大大简化了响应写法:
class APIResponse(Response):
# 重写的APIResponse类是继承了restframework中的Response 再此基础上重写多个初始化值
# 设置默认参数
def __init__(self, data_status=0, data_msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
data = {
'status': data_status,
'msg': data_msg,
}
# results可能是False、0等数据,这些数据某些情况下也会作为合法数据返回
if results is not None:
data['results'] = results
data.update(kwargs)
# 继承并重用父类的 的init方法,把自定义的data数据体传进去
# 父类的:__init__(self, data=None, status=None,template_name=None, headers=None,exception=False, content_type=None):
# 异常响应,默认为False
super().__init__(data=data, status=http_status, headers=headers, exception=exception)
class CarAPIView(APIView):
def get(self,request,*args,**kwargs):
print(request.query_params)
print(request.data)
# print(request.method)
# print(request._request.method)
return APIResponse(data={"msg":"apiview get ok"})