DRF 3 请求响应异常处理
3,请求,响应,异常
3.1 DRF Request
from rest_framework.request import Request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
Request常用属性
1).data
request.data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和 request.FILES
属性,但提供如下特性:
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求方式解析后的数据
- 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params
request.query_params
与Django标准的request.GET
相同,只是更换了更正确的名称而已。
3).请求头的数据
ip地址和token都在http的请求头里
request.META.get('HTTP_REMOTE_ADDR') 取IP地址
request.META.get('HTTP_AUTHORIZATION') 取token
补充:Django源生request的使用
1.获取请求路径中的查询字符串参数,可以通过request.GET属性获取,返回QueryDict对象
2.前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象
3.非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取原始数据自己处理
4.可以通过request.META属性获取请求头headers中的数据
5.HttpRequest对象的属性GET、POST都是QueryDict类型的对象
方法get():根据键获取值,如果一个键同时拥有多个值将获取最后一个值
方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值
3.2 DRF Response
from rest_framework.response import Response
REST framework提供了一个响应类Response
,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
REST framework提供了Renderer
渲染器,用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
可以在rest_framework.settings查找所有的drf默认配置项
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
使用
Response(data, status=None, template_name=None, headers=None, content_type=None)
data
数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer
渲染器处理data
。
data
不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer
序列化器序列化处理后(转为了Python字典类型)再传递给data
参数。
参数说明:
data
: 前段要展示的数据,def的data格式不友好status
: 状态码,给浏览器识别的,浏览器会更据改状态码做出反应,默认200;template_name
: 模板名称,如果使用HTMLRenderer
时需指明;headers
: 用于存放响应头信息的字典;content_type
: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。
Response的常用属性
1).data
传给response对象的序列化后,但尚未render处理的数据
2).status_code
状态码的数字
3).content
经过render处理后的响应数据
状态码
为了方便设置状态码,REST framewrok在rest_framework.status
模块中提供了常用状态码常量。
1)信息告知 - 1xx
2)成功 - 2xx
3)重定向 - 3xx
4)客户端错误 - 4xx
5)服务器错误 - 5xx
自定义Response
class APIResponse(Response):
def __init__(self,code=100,msg='成功',data=None,status=None,headers=None,**kwargs):
dic = {'code': code, 'msg': msg}
if data:
dic = {'code': code, 'msg': msg,'data':data}
dic.update(kwargs)
super().__init__(data=dic, status=status,headers=headers)
# 自己封装的参数,code,msg,data都会被放进一个字典中,然后将该字典打包传个drf的Response的data
# code是指自定义的状态码,为了说明用户的操作结果,浏览器不会根据它做出反应,是写在data里的
# status是服务器处理状态码,浏览器会根据该状态码做出反应,一般不需要手动填写
3.3 异常处理
drf源生异常处理
这是drf的settings的配置,若要更改默认配置需要在项目的settings里重新配置.
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
源码:
from rest_framework.views import exception_handler
def exception_handler(exc, context):
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
前后端分离的项目,如果出现异常,drf能处理的异常非常有限,剩下的全部交由Django处理,但是Django的处理方式非常不友好,返回的不是json格式(这不符合restfull规范),我们要做的就是自定义一个异常处理器捕获这些异常,并返回json格式.
自定义异常处理
作用:
1,捕获其他异常,输出为json格式使其符合restfull规范
2,记录日志
自定义异常
# 自定义异常处理的方法
from rest_framework.views import exception_handler
from rest_framework.response import APIResponse # 自定义包装的response
from rest_framework import status
def my_exception_handler(exc, context):
response=exception_handler(exc, context)
# 两种情况,一个是None,drf没有处理
#response对象,django处理了,但是处理的不符合咱们的要求
if not response: #drf没处理的情况,还可以更细力度的捕获异常
if isinstance(exc, ZeroDivisionError):
return APIResponse(data={'status': 777, 'msg': "除以0的错误" + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
return APIResponse(data={'status':999,'msg':str(exc)},status=status.HTTP_400_BAD_REQUEST)
else: # drf处理的异常,我们包装一下格式
return APIResponse(data={'status':888,'msg':response.data.get('detail')},status=status.HTTP_400_BAD_REQUEST)
# 全局配置setting.py
'EXCEPTION_HANDLER': 'app01.app_auth.my_exception_handler',