Django REST framework--渲染器
- Django REST framework--渲染器
- 自定义接口规范
- 渲染器基本原理
- Django 项目debug调试技巧
 
- 异常信息处理
- 配置异常处理模块
- 自定义异常处理
 
 
 
Django REST framework–渲染器
自定义接口规范
目前使用的是REST框架默认的返回格式,类似这种
[
    {
        "id": 38,
        "method": 0,
        "url": "/api/teacher/",
        "params": null,
        "headers": null,
        "cookies": null,
        "data": {
            "name": "小王老师",
            "courses": "英语",
            "address": "广东深圳"
        },
        "json": null,
        "step": null
    },
    {
        "id": 39,
        "method": 0,
        "url": "/api/teacher/",
        "params": null,
        "headers": null,
        "cookies": null,
        "data": {
            "name": "小王老师",
            "courses": "英语",
            "address": "广东深圳"
        },
        "json": null,
        "step": null
    }
]    
没有自定义响应字段封装,不利于前端的渲染和接口验证
希望接口在正确的时候返回
{"msg":"success","retcode":200,"retlist":[...]}
接口在错误的时候返回
{"msg":"error","retcode":404,"error":error_msg}
想要弄成类似这样的效果需要自定义DRF异常返回和自定义数据返回格式,在项目根目录下新增一个文件夹utils,在该文件夹下新建renderers.py文件并写入以下代码
from rest_framework.renderers import JSONRenderer
# 自定义渲染器
class MyRenderer(JSONRenderer):
    # 重构 render方法
    def render(self,data,accepted_media_type=None,renderer_context=None):
        # 响应状态码
        status_code = renderer_context['response'].status_code
        # 正常返回 status_code 以2开头
        if str(status_code).startswith('2'):
            res = {'msg':"success",'retcode':status_code,'retlist':data}
            # 返回父类方法
            return super().render(res,accepted_media_type,renderer_context)
        # 异常情况
        else:
            res = {'msg':"error",'retcode':status_code}
            return super().render(res,accepted_media_type,renderer_context)
在settings.py文件底部添加DRF全局配置
# rest框架配置
REST_FRAMEWORK = {
    # 默认的渲染器
    'DEFAULT_RENDERER_CLASSES': (
        # 'rest_framework.renderers.JSONRenderer',
        # 'rest_framework.renderers.BrowsableAPIRenderer',
        'utils.renderers.MyRenderer',
 )
}
执行命令python manage.py runserver 0.0.0.0:8888启动项目,在浏览器中输入http://127.0.0.1:8888/requests/38,可以发现返回带响应字段的json数据
{
    "msg": "success",
    "retcode": 200,
    "retlist": {
        "id": 38,
        "method": 0,
        "url": "/api/teacher/",
        "params": null,
        "headers": null,
        "cookies": null,
        "data": {
            "name": "小王老师",
            "courses": "英语",
            "address": "广东深圳"
        },
        "json": null,
        "step": null
    }
}
在浏览器中输入不存在的id,返回失败,http://127.0.0.1:8888/requests/38,可以发现返回带异常字段的json数据
{"msg":"error","retcode":404}
若在浏览器中输入http://127.0.0.1:8888/requests/,则返回数据库中查询的所有数据,器json数据内容会放到以retlist为名的列表里
{
    "msg": "success",
    "retcode": 200,
    "retlist": [
        {
            "id": 38,
            "method": 0,
            "url": "/api/teacher/",
            "params": null,
            "headers": null,
            "cookies": null,
            "data": {
                "name": "小王老师",
                "courses": "英语",
                "address": "广东深圳"
            },
            "json": null,
            "step": null
        },
        {
            "id": 39,
            "method": 0,
            "url": "/api/teacher/",
            "params": null,
            "headers": null,
            "cookies": null,
            "data": {
                "name": "小王老师",
                "courses": "英语",
                "address": "广东深圳"
            },
            "json": null,
            "step": null
        }
    ]
}
渲染器基本原理
序列化在返回数据后并不是直接做为响应数据,而是经过渲染器的渲染,生成不同格式的响应内容,如html或json格式。REST本身支持HTML和json格式,有内置的渲染器。那么如果想要返回定制化的内容就要重写渲染器,按照指定的格式进行响应

重构渲染器就是重写父类渲染器的render方法,在renderers.py文件中,就是通过定义一个MyRenderer类来自定义渲染器,然后通过自定义render函数,来重写父类渲染器的render方法
def render(self,data,accepted_media_type=None,renderer_context=None):
自定义render函数中有 data、accepted_media_type和renderer_context,其中accepted_media_type和renderer_context默认为None,也就是说这两个参数是可选的。
data指响应数据(序列化器的.data属性),等同于renderer_context[" response "].data的值
accepted_media_type是由内容协商阶段确定的所接受的媒体类型。根据客户端的 Accept: 头,这可能比渲染器的 media_type 属性更具体,可能包括媒体类型参数。例如 "application/json; nested=true" 。
renderer_context 是一个由view提供的上下文信息的字典。默认情况下这个字典会包括以下键: view , request , response , args , kwargs 。
-  renderer_context[" view "]对应调用的视图函数
-  renderer_context[" request "]对应本次请求对象,包含所有请求数据,如请求头,请求参数等等
-  renderer_context[" response "]对应本次响应对象,包含所有响应数据,如响应头,状态码,响应数据等等 
最后,通过super()将参数内容返回父类方法。
如果响应状态码status_code 以2开头,则说明响应成功,将{'msg':"success",'retcode':status_code,'retlist':data}作为data数据返回给父类方法;
如果响应状态码status_code 不以2开头,则说明响应失败,将{'msg':"error",'retcode':status_code}作为data数据返回给父类方法
Django 项目debug调试技巧
点击pycharm右上角,选中Edit Configurations,添加Django Server,编辑运行配置内容,应用成功后,点击调试按钮进行调试

在return super().render(res,accepted_media_type,renderer_context)这行代码打上断点,然后在浏览器中输入http://127.0.0.1:8888/requests/38,通过pycharm查看debug控制台打印内容,了解数据的流转过程及数据内容

异常信息处理
当前情况下我们智能获取到正常情况下返回的数据,如果想返回异常信息,需要了解下REST异常处理机制,
REST默认情况可以处理的异常有:
-  在REST framework内部产生的 APIException 的子类异常。 
-  原生Django的 Http404 异常. 
-  原生Django的 PermissionDenied 异常. 

如果想要获取异常数据,需要从异常处理器中提取,REST默认的是 exception_handler,当触发异常时,获取到异常的响应信息,便可以自由定义其格式。
配置异常处理模块
在settings.py文件底部添加DRF全局配置
# rest框架配置
REST_FRAMEWORK = {
   # 全局配置异常模块
   'EXCEPTION_HANDLER': 'utils.exception.my_exception_handler',
   # 默认的渲染器
   'DEFAULT_RENDERER_CLASSES': (
       'utils.renderers.MyRenderer',
   )
}
自定义异常处理
增加异常处理
在utils文件夹下新建exception.py文件并写入以下代码
from rest_framework.views import exception_handler, Response
def my_exception_handler(exc, context):     # exc:异常信息   context:上下文
    # 首先调用REST framework默认的异常处理,以获得标准的错误响应。
    error_response = exception_handler(exc, context)
    if error_response:
        # 成功捕获到异常的情况
        error_response.data['msg'] = 'error'  # 标记
        error_response.data['retcode'] = error_response.status_code  # 状态码
        error_response.data['error'] = str(exc)  # 详细错误原因
        error_response.data.pop('detail')  # detail的异常信息比较简单
    return error_response
更新渲染器逻辑
from rest_framework.renderers import JSONRenderer
# 自定义渲染器
class MyRenderer(JSONRenderer):
    # 重构 render方法
    def render(self,data,accepted_media_type=None,renderer_context=None):
        # 默认把data作为响应数据
        resp_content = data
        if renderer_context:
            # 响应状态码
            status_code = renderer_context['response'].status_code
            # 正常返回 status_code 以2开头
            if str(status_code).startswith('2'):
                # 判断响应内容是否是列表形式,如果不是列表形式,则变成列表形式以对应接口要求
                if not isinstance(resp_content, list):
                    resp_content = [resp_content]
                res = {'msg':"success",'retcode':status_code,'retlist':data}
                # 返回父类方法
                return super().render(res,accepted_media_type,renderer_context)
            # 异常情况
            return super().render(resp_content,accepted_media_type,renderer_context)



















