likes
comments
collection
share

FastApi(自用脚手架)+Snowy搭建后台管理系统(12)-再谈RequestValidationError参数校验中文错误提示

作者站长头像
站长
· 阅读数 12

问题反思

在前面之中,关于输出错误中文的提示中,我自己思考了一下,其实有陷入了一个小小的误区,就是为什么我需要依赖上下文中透传对于的模型对象来匹配错误信息呢?其实最简单的方式,就是针对的错误类型来对于错误的信息即可了!

首先如下代码所示:

def get_any_en_error(_request: Request, exc: Union[RequestValidationError, ValidationError]):
    undefined = "{loction} type for: {name} is: {meessage} ,error type is:{errortypr}"
    #print("输出!!",exc.body)
    try:
        for err in exc.errors():
            if err["type"] == 'value_error.jsondecode':
                err["msg"] = _any_error[err["type"]]
                return err["msg"]
            loction = err["loc"][0]
            name = err["loc"][1]
            _value = None
            try:
                _value = err['ctx']
            except:
                pass

            limit_value = None
            if _value and 'limit_value' in _value:
                limit_value = _value['limit_value']
            if limit_value:
                err["msg"] = _any_error[err["type"]].format(loction=loction, name=name,
                                                            limit_value=limit_value) if err[
                                                                                            "type"] in _any_error else \
                    undefined.format(loction=loction, name=name, meessage=err["msg"], errortypr=err["type"])
            else:
                err["msg"] = _any_error[err["type"]].format(loction=loction, name=name) if err[
                                                                                               "type"] in _any_error else \
                    undefined.format(loction=loction, name=name, meessage=err["msg"], errortypr=err["type"])
            return err["msg"]
    except:
        return APIValidationError.from_pydantic_only_one_message(exc)

首先在上面的代码中,我们可以从exc.errors()里面可以获取到对应的错误类型、错误字段名称、错误字段的位置(路径参数,还是body参数,还是查询参数等)、错误类型对于的模板描述的错误模板信息。

上述几点意见足以满足我们的自定义一个错误模板,来配置对应的错误信息了,当然部分的特殊的错误类型除外,比如:

if err["type"] == 'value_error.jsondecode':

问题解决

从上面中既然我们的都可以从错误信息中获取到非常的多的信息了,我们只需要自定义一个错误模板即可,如下代码所示:

from typing import Union

from fastapi import Request
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError

from snowy_src.snowy_common.snowy_errors.validation_error import APIValidationError

# 常见的错误模板映射
_any_error = {
    'value_error.jsondecode': '传入的参数字段出现转换错误,请核对是否存在类型错误',
    'value_error.missing': '{loction} 类型 {name} 参数字段为必传参数',
    'value_error.any_str.max_length': '{loction} 类型 {name} 参数字段字符串长度最多{limit_value}位',
    'value_error.any_str.min_length': '{loction} 类型 {name} 参数字段字符串长度至少{limit_value}位以上',
    'value_error.number.not_gt': '{loction} 类型 {name} 参数字段信息必须大于{limit_value}',
    'value_error.number.not_le': '{loction} 类型 {name} 参数字段信息必须小于等于{limit_value}',
    # =============================================
    # type_error.none.not_allowed
    'type_error.none.not_allowed': '{loction} 类型 {name} 参数字段值必须不能空值',
    "type_error.integer": "{loction} 类型 {name} 参数必须是一个int类型的参数",
    "type_error.float": "{loction} 类型 {name} 参数必须是一个float类型的参数",
}

目前我个人就收集了这几个常见的错误,它范围值类型错误和类型错误,可能还有其他错误的类型,欢迎大家可以补充。

有了对应的错误类型后,我们只需要进行匹配类型,然后进行替换错误信息即可。如下核心的关键代码:

...省份部分代码
  limit_value = None
            if _value and 'limit_value' in _value:
                limit_value = _value['limit_value']
            if limit_value:
                err["msg"] = _any_error[err["type"]].format(loction=loction, name=name,
                                                            limit_value=limit_value) if err[
                                                                                            "type"] in _any_error else \
                    undefined.format(loction=loction, name=name, meessage=err["msg"], errortypr=err["type"])
            else:
                err["msg"] = _any_error[err["type"]].format(loction=loction, name=name) if err[
                                                                                               "type"] in _any_error else \
                    undefined.format(loction=loction, name=name, meessage=err["msg"], errortypr=err["type"])
            return err["msg"]

问题扩展

对于其他错误的类型,我们可以通过相关的函数来遍历获取对于的错误类型即可,如下核心的代码:

    @classmethod
    def _get_pydantic_messages_dict(cls) -> Dict[str, str]:
        from pydantic import errors

        messages = (
            re.sub(r"\{.+\}", "{}", getattr(errors, name).msg_template)
            for name in errors.__all__
            if hasattr(getattr(errors, name), "msg_template")
        )
        return {value: value for value in messages}

    @classmethod
    def _get_pydantic_messages_dict_code(cls) -> Dict[str, str]:
        from pydantic import errors

        messages = (
            "value_error." + re.sub(r"\{.+\}", "{}", getattr(errors, name).code)
            for name in errors.__all__
            if hasattr(getattr(errors, name), "code")
        )
        return {value: value for value in messages}

得到的对应的一些错误模板信息如下:


value_error_error_msg_unwal = {'value_error.none.not_allowed': 'value_error.none.not_allowed',
                               'value_error.none.allowed': 'value_error.none.allowed',
                               'value_error.const': 'value_error.const',
                               'value_error.not_none': 'value_error.not_none',
                               'value_error.url': 'value_error.url',
                               'value_error.url.scheme': 'value_error.url.scheme',
                               'value_error.url.userinfo': 'value_error.url.userinfo',
                               'value_error.url.host': 'value_error.url.host',
                               'value_error.url.port': 'value_error.url.port',
                               'value_error.url.extra': 'value_error.url.extra',
                               'value_error.enum_instance': 'value_error.enum_instance',
                               'value_error.int_enum_instance': 'value_error.int_enum_instance',
                               'value_error.enum': 'value_error.enum',
                               'value_error.path.not_exists': 'value_error.path.not_exists',
                               'value_error.path.not_a_file': 'value_error.path.not_a_file',
                               'value_error.path.not_a_directory': 'value_error.path.not_a_directory',
                               'value_error.tuple.length': 'value_error.tuple.length',
                               'value_error.list.min_items': 'value_error.list.min_items',
                               'value_error.list.max_items': 'value_error.list.max_items',
                               'value_error.list.unique_items': 'value_error.list.unique_items',
                               'value_error.set.min_items': 'value_error.set.min_items',
                               'value_error.set.max_items': 'value_error.set.max_items',
                               'value_error.frozenset.min_items': 'value_error.frozenset.min_items',
                               'value_error.frozenset.max_items': 'value_error.frozenset.max_items',
                               'value_error.any_str.min_length': 'value_error.any_str.min_length',
                               'value_error.any_str.max_length': 'value_error.any_str.max_length',
                               'value_error.str.regex': 'value_error.str.regex',
                               'value_error.number.not_gt': 'value_error.number.not_gt',
                               'value_error.number.not_ge': 'value_error.number.not_ge',
                               'value_error.number.not_lt': 'value_error.number.not_lt',
                               'value_error.number.not_le': 'value_error.number.not_le',
                               'value_error.number.not_multiple': 'value_error.number.not_multiple',
                               'value_error.decimal.not_finite': 'value_error.decimal.not_finite',
                               'value_error.decimal.max_digits': 'value_error.decimal.max_digits',
                               'value_error.decimal.max_places': 'value_error.decimal.max_places',
                               'value_error.decimal.whole_digits': 'value_error.decimal.whole_digits',
                               'value_error.date.not_in_the_past': 'value_error.date.not_in_the_past',
                               'value_error.date.not_in_the_future': 'value_error.date.not_in_the_future',
                               'value_error.uuid.version': 'value_error.uuid.version',
                               'value_error.arbitrary_type': 'value_error.arbitrary_type',
                               'value_error.class': 'value_error.class', 'value_error.subclass': 'value_error.subclass',
                               'value_error.json': 'value_error.json',
                               'value_error.regex_pattern': 'value_error.regex_pattern',
                               'value_error.dataclass': 'value_error.dataclass',
                               'value_error.payment_card_number.digits': 'value_error.payment_card_number.digits',
                               'value_error.payment_card_number.luhn_check': 'value_error.payment_card_number.luhn_check',
                               'value_error.payment_card_number.invalid_length_for_brand': 'value_error.payment_card_number.invalid_length_for_brand',
                               'value_error.discriminated_union.missing_discriminator': 'value_error.discriminated_union.missing_discriminator',
                               'value_error.discriminated_union.invalid_discriminator': 'value_error.discriminated_union.invalid_discriminator'}

error_msg = {'field required': 'field required', 'extra fields not permitted': 'extra fields not permitted',
             'none is not an allowed value': 'none is not an allowed value', 'value is not none': 'value is not none',
             'value is not None': 'value is not None',
             'value could not be parsed to a boolean': 'value could not be parsed to a boolean',
             'byte type expected': 'byte type expected', 'value is not a valid dict': 'value is not a valid dict',
             'value is not a valid email address': 'value is not a valid email address',
             'invalid or missing URL scheme': 'invalid or missing URL scheme',
             'URL scheme not permitted': 'URL scheme not permitted',
             'userinfo required in URL but missing': 'userinfo required in URL but missing',
             'URL host invalid': 'URL host invalid',
             'URL host invalid, top level domain required': 'URL host invalid, top level domain required',
             'URL port invalid, port cannot exceed 65535': 'URL port invalid, port cannot exceed 65535',
             'URL invalid, extra characters found after valid URL: {}': 'URL invalid, extra characters found after valid URL: {}',
             '{} is not a valid Enum instance': '{} is not a valid Enum instance',
             '{} is not a valid IntEnum instance': '{} is not a valid IntEnum instance',
             'value is not a valid integer': 'value is not a valid integer',
             'value is not a valid float': 'value is not a valid float',
             'value is not a valid path': 'value is not a valid path',
             'file or directory at path "{}" does not exist': 'file or directory at path "{}" does not exist',
             'path "{}" does not point to a file': 'path "{}" does not point to a file',
             'path "{}" does not point to a directory': 'path "{}" does not point to a directory',
             'ensure this value contains valid import path or valid callable: {}': 'ensure this value contains valid import path or valid callable: {}',
             'value is not a valid sequence': 'value is not a valid sequence',
             'value is not a valid list': 'value is not a valid list',
             'value is not a valid set': 'value is not a valid set',
             'value is not a valid frozenset': 'value is not a valid frozenset',
             'value is not a valid tuple': 'value is not a valid tuple',
             'wrong tuple length {}': 'wrong tuple length {}',
             'ensure this value has at least {} items': 'ensure this value has at least {} items',
             'ensure this value has at most {} items': 'ensure this value has at most {} items',
             'the list has duplicated items': 'the list has duplicated items',
             'ensure this value has at least {} characters': 'ensure this value has at least {} characters',
             'ensure this value has at most {} characters': 'ensure this value has at most {} characters',
             'str type expected': 'str type expected',
             'string does not match regex "{}"': 'string does not match regex "{}"',
             'ensure this value is greater than {}': 'ensure this value is greater than {}',
             'ensure this value is greater than or equal to {}': 'ensure this value is greater than or equal to {}',
             'ensure this value is less than {}': 'ensure this value is less than {}',
             'ensure this value is less than or equal to {}': 'ensure this value is less than or equal to {}',
             'ensure this value is a multiple of {}': 'ensure this value is a multiple of {}',
             'value is not a valid decimal': 'value is not a valid decimal',
             'ensure that there are no more than {} digits in total': 'ensure that there are no more than {} digits in total',
             'ensure that there are no more than {} decimal places': 'ensure that there are no more than {} decimal places',
             'ensure that there are no more than {} digits before the decimal point': 'ensure that there are no more than {} digits before the decimal point',
             'invalid datetime format': 'invalid datetime format', 'invalid date format': 'invalid date format',
             'date is not in the past': 'date is not in the past',
             'date is not in the future': 'date is not in the future', 'invalid time format': 'invalid time format',
             'invalid duration format': 'invalid duration format',
             'value is not a valid hashable': 'value is not a valid hashable',
             'value is not a valid uuid': 'value is not a valid uuid',
             'uuid version {} expected': 'uuid version {} expected',
             'instance of {} expected': 'instance of {} expected', 'a class is expected': 'a class is expected',
             'subclass of {} expected': 'subclass of {} expected', 'Invalid JSON': 'Invalid JSON',
             'JSON object must be str, bytes or bytearray': 'JSON object must be str, bytes or bytearray',
             'Invalid regular expression': 'Invalid regular expression',
             'instance of {}, tuple or dict expected': 'instance of {}, tuple or dict expected',
             '{} is not callable': '{} is not callable',
             'value is not a valid IPv4 or IPv6 address': 'value is not a valid IPv4 or IPv6 address',
             'value is not a valid IPv4 or IPv6 interface': 'value is not a valid IPv4 or IPv6 interface',
             'value is not a valid IPv4 or IPv6 network': 'value is not a valid IPv4 or IPv6 network',
             'value is not a valid IPv4 address': 'value is not a valid IPv4 address',
             'value is not a valid IPv6 address': 'value is not a valid IPv6 address',
             'value is not a valid IPv4 network': 'value is not a valid IPv4 network',
             'value is not a valid IPv6 network': 'value is not a valid IPv6 network',
             'value is not a valid IPv4 interface': 'value is not a valid IPv4 interface',
             'value is not a valid IPv6 interface': 'value is not a valid IPv6 interface',
             'value is not a valid color: {}': 'value is not a valid color: {}',
             'value is not a valid boolean': 'value is not a valid boolean',
             'card number is not all digits': 'card number is not all digits',
             'card number is not luhn valid': 'card number is not luhn valid', 'Length for a {}': 'Length for a {}',
             'could not parse value and unit from byte string': 'could not parse value and unit from byte string',
             'could not interpret byte unit: {}': 'could not interpret byte unit: {}',
             'Discriminator {} is missing in value': 'Discriminator {} is missing in value',
             'No match for discriminator {})': 'No match for discriminator {})'
             }

为了更好的进行输出相关错误信息,我们可以友好的进行输出,方便改造我们的错误模板,通过下面的方式输出对应的错误类型和错误信息:

 return APIValidationError.from_pydantic_only_one_message(exc)

至此,关于【再谈RequestValidationError参数校验中文错误提示】相关介绍分享已完成!以上内容分享纯属个人经验,仅供参考!

文笔有限,如有笔误或错误!欢迎批评指正!感谢各位大佬!有什么问题也可以随时交流!

结尾

END

简书:www.jianshu.com/u/d6960089b…

掘金:juejin.cn/user/296393…

公众号:微信搜【程序员小钟同学】

新开QQ群号,欢迎随时加群交流,相互学习。QQ群号:247491107

小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822