V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
kayseen
V2EX  ›  Python

关于 drf 框架中抛出异常的正确姿势

  •  
  •   kayseen · 2020-01-09 09:41:32 +08:00 · 4850 次点击
    这是一个创建于 1806 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,之前使用的是 flask 框架, 抛出的异常都是自定义状态码和详细的异常信息, 抛出异常的格式为:

    {
      "code": "1001",
      "data": {},
      "response": "请求不符合规范",
      "time": 2
    }
    
    

    然后现在学习使用 drf 框架, 比如在序列化器校验的时候抛出一个异常:

    raise serializers.ValidationError('登录密码错误')
    

    这个时候通过 postman 去故意触发这个报错,返回信息如下:

    {
        "non_field_errors": [
            "登录密码错误"
        ]
    }
    

    然后我在序列化器中定义一个方法def validate_username去检验用户名称,在该方法中定义一个异常:

    raise serializers.ValidationError('用户名称不符合规范')
    

    然后故意去触发该异常,返回的信息如下:

    {
        "username": [
            "用户名称不符合规范"
        ],
    }
    

    我现在就有点蒙了, 为什么返回异常的字段还会不断的变化?或者我应该怎么样捕获到它所有的异常来统一处理呢?网上有人说定义方法重新捕获,但是像如上的这种情况,连异常的字段信息都不一致,我该如何捕捉到具体的报错信息呢?

    18 条回复    2020-02-27 22:44:18 +08:00
    lynskylate
        1
    lynskylate  
       2020-01-09 09:48:38 +08:00 via Android
    你定义的 validate username 检查的是定义的 model 中 username 这个 field,drf 对于每个 field 都会检查,抛出的错误中 key 是检查失败的 field 名。
    Jammar
        2
    Jammar  
       2020-01-09 09:53:25 +08:00
    抛出异常上层捕获异常
    kayseen
        3
    kayseen  
    OP
       2020-01-09 09:55:04 +08:00
    @lynskylate
    是的, 我在定义的`def validate`方法中抛出的异常字段名为`non_field_errors`, 请教一下关于 drf 的异常你是怎么处理的?
    kayseen
        4
    kayseen  
    OP
       2020-01-09 09:57:41 +08:00
    @Jammar
    你好, 这个思想我知道,我在上层捕获到的异常具体为:
    ```python
    {'non_field_errors': [ErrorDetail(string='登录密码错误', code='invalid')]}
    ```
    然后就获取不到 string 的具体信息了......
    lynskylate
        5
    lynskylate  
       2020-01-09 10:00:13 +08:00 via Android
    @kayseen drf 可以定义统一处理异常的函数,也有定义处理响应的地方。
    hccsoul
        6
    hccsoul  
       2020-01-09 10:52:12 +08:00
    第一瞬间看成 dnf,臭打游戏的没救了--
    Hstar
        7
    Hstar  
       2020-01-09 10:59:31 +08:00
    建议你手动写一个 view,直接 raise Exception 的那种。

    你所说的字段变化是 ValidationError 的特性,分别理解比较容易。
    stephenyin
        8
    stephenyin  
       2020-01-09 12:10:10 +08:00
    看见中出就进来了
    Rand01ph
        9
    Rand01ph  
       2020-01-09 13:27:02 +08:00
    我这边都是直接在业务逻辑 raise 自定义异常,然后定义自定义异常处理函数,根据不同的异常生成返回.
    Jammar
        10
    Jammar  
       2020-01-09 15:51:56 +08:00
    设置里面 drf 的设置配置这个字段'EXCEPTION_HANDLER'
    kayseen
        12
    kayseen  
    OP
       2020-01-10 10:47:06 +08:00
    @hccsoul

    @stephenyin

    你们可真秀啊~
    kayseen
        13
    kayseen  
    OP
       2020-01-10 10:48:20 +08:00
    @Rand01ph
    你好,我想问的好像就是你说的这个,请问你是不是在下面的这个函数中定义的处理?

    ```python
    from rest_framework.views import exception_handler

    def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    # Now add the HTTP status code to the response.
    if response is not None:
    response.data['status_code'] = response.status_code

    return response
    ```
    Rand01ph
        14
    Rand01ph  
       2020-01-14 10:04:31 +08:00
    @kayseen 是的,是这个功能.
    然后配置 `EXCEPTION_HANDLER`
    leinal
        15
    leinal  
       2020-02-17 16:37:56 +08:00
    想问下楼主,这个问题搞定了吗?目前也是这个问题。
    kayseen
        16
    kayseen  
    OP
       2020-02-18 10:14:53 +08:00 via Android
    没有(>﹏<) 你搞定的话麻烦说下
    kayseen
        17
    kayseen  
    OP
       2020-02-18 10:15:18 +08:00 via Android
    @leinal 没有(>﹏<) 你搞定的话麻烦说下
    sxy960806
        18
    sxy960806  
       2020-02-27 22:44:18 +08:00
    def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if response:
    detail = response.data.get('detail')
    non_field_errors = response.data.get('non_field_errors')
    # 异常响应
    if detail:
    return Response({'response': detail, 'data': {}, 'code': '1001'})
    elif non_field_errors:
    return Response({'response': non_field_errors[0], 'data': {}, 'code': '1001'})
    else:
    return Response({'response': '接口参数错误', 'data': {}, 'code': '1001', 'tips': response.data})
    return response


    可以按照我这个思路来添加异常处理
    效果如下:
    # 登陆失败
    {
    "response": "无法使用提供的认证信息登录。",
    "data": {},
    "code": "1001"
    }
    # 开发阶段字段不完整或字段校验错误
    {
    "response": "接口参数错误",
    "data": {},
    "code": "1001",
    "tips": {
    "end_date": [
    "该字段是必填项。"
    ],
    "priority": [
    "该字段是必填项。"
    ],
    "auditor": [
    "该字段是必填项。"
    ]
    }
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3211 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 12:36 · PVG 20:36 · LAX 04:36 · JFK 07:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.