APIView请求生命周期
- APIView类继承View类,重写了as_view和dispatch方法
- 重写的as_view方法,主体还是View的as_view,只是在返回视图view函数地址时,局部禁用csrf认证
# 继承了View
class APIView(View):
@classmethod
def as_view(cls, **initkwargs)
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
# 可见, 重写的as_view的主体还是View的as_view
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# 就增加的一个功能, 就是局部禁用crsf认证
return csrf_exempt(view)
- 重写的dispatch方法
- 在执行请求逻辑前:请求模块(二次封装request)、解析模块(三种数据包格式的数据解析
- 在执行请求逻辑中:异常模块(执行出现任何异常交给异常模块处理)
- 在执行请求逻辑后:响应模块(二次封装request)、渲染模块(响应的数据能在json和页面两种形式进行渲染)
"""
1.在执行请求逻辑前:
请求模块(二次封装request), 解析模块(三种数据包格式的数据解析)
2.在执行请求逻辑中:
异常模块(处理执行过程中的任何异常)
3.在执行请求逻辑后:
响应模块(二次封装response), 渲染模块(响应的数据能通过JSON和页面两种形式进行渲染)
"""
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 1.请求模块和解析模块: initialize_request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
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.异常模块, 捕获并处理异常: handle_exception
except Exception as exc:
response = self.handle_exception(exc)
# 3.响应模块和渲染模块: finalize_response
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
请求模块
- 将wsgi的request对象转化成drf的Request类的对象
- 封装后的request对象完全兼容wsgi的request对象,并且将原request保存在新
request._request
- 重写格式化请求数据存放位置
- 拼接参数:
request.query_params
- 数据包参数:
request.data
- 拼接参数:
class Request:
"""
Wrapper allowing to enhance a standard `HttpRequest` instance.
Kwargs:
- request(HttpRequest). The original request instance.
- parsers_classes(list/tuple). The parsers to use for parsing the
request content.
- authentication_classes(list/tuple). The authentications used to try
authenticating the request's user.
"""
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
# drf的request兼容wsgi的request
self._request = request
# drf的Request类下面的__getattr__方法
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
# drf的request中没有的属性和方法, 会通过反射去wsgi中的request对象中寻找
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
# 可见, 这个query_params就是_request.GET
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
# request.data是方法属性, 返回request._full_data的值
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
解析模块
只处理数据包参数 - form-data, urlencoded, json
- 全局配置所有视图类的解析方式,解析配置可以配置三种
# 在项目的settings.py文件中进行如下配置
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
# 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
# drf下的视图类继承APIView
class BookApiView(APIView):
# 为当前视图类配置解析类
parser_classes = [JSONParser, FormParser, MultiPartParser]
- 配置的查找顺序:局部(视图类的类属性) =》全局(settings文件的drf配置) =》默认(drf的默认配置)
# 因此我们的视图类可以自定义parser_classes属性, 来覆盖父类的属性
class APIView(View):
# APIView的默认的解析属性
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
注意:该模块仅供了解,但是全局与局部配置是重点
响应模块
data
:响应数据status
:响应的网络状态码template_name
:drf完成前后端不分离返回页面,但是就不可以返回dataheaders
:响应头,一般不规定,默认exception
:一般异常响应,会将其设置成True,默认Falsecontent_type
:默认是appliacation/json
,不需要处理
class Response(SimpleTemplateResponse):
"""
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super().__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = data
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in headers.items():
self[name] = value
渲染模块
- Postman请求结果是json, 浏览器请求的结果是页面
- 同解析模块一样, render渲染模块也可以进行局部配置和全局配置
- 默认配置
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
- 局部配置
# views.py
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer
# drf下的视图类继承APIView
class BookApiView(APIView):
# 为当前视图类配置渲染类
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
- 全局配置
# settings.py
REST_FRAMEWORK = {
# 全局配置渲染类:适用于所有视图类
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer', # 上线后尽量关闭
],
异常模块
- drf默认的异常处理模块只处理客户端请求异常
# dispatch方法中的异常捕获
except Exception as exc:
response = self.handle_exception(exc)
# APIView下面的handle.exception异常处理方法
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request)
if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
# 获取处理异常的函数: 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
exception_handler = self.get_exception_handler()
# 给异常处理提供额外的参数
context = self.get_exception_handler_context()
response = exception_handler(exc, context) # exc: 异常对象, content: 视图对象和请求对象
if response is None:
self.raise_uncaught_exception(exc)
response.exception = True
return response
# 默认的异常处理函数只处理客户端的请求异常
def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `Http404` and `PermissionDenied` exceptions.
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
if isinstance(exc, Http404):
exc = exceptions.NotFound()
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = {'detail': exc.detail}
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
return None
- 自定义异常处理方法
# 1.在app文件夹下新建exception.py文件
# 2.在exception.py书写自定义异常处理方法
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
# 主体还是drf默认的异常处理方法
response = drf_exception_handler(exc, context)
# 按照一定格式返回异常信息
detail = f"{context.get('view')}-{context.get('request')}-{exc}"
# 服务端错误
if not response:
return Response({
'detail': detail
})
else:
response.data = {'detail': detail}
return response
# 3.在项目settings.py文件下进行如下配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}