likes
comments
collection
share

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

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

前言

长久以来比较困扰的一个关于【pydantic的问题参数校验提示的问题】突发奇想的情况,有一个迂回的实现方案,它可以实现的效果就是类似我们很久以前渴望实现的类似wtform类似的定义的自定义的【中文】提示。

wtform自定义错误提示

以下示例是wtform的十点类型错误时候定义的错误信息提示。

from core.forms import BaseForm,validate_form,validate_back_form
from wtforms import DateTimeField, PasswordField, FieldList, IntegerField, StringField
from wtforms.validators import DataRequired, Regexp, EqualTo, length, Optional, NumberRange

class LoginForm(BaseForm):
    username = StringField(validators=[DataRequired(message='username 必须传入')])
    password = StringField(validators=[DataRequired(message='password 必须传入')])

class SysPermissionForm(BaseForm):
    token = StringField(validators=[DataRequired(message='token 必须传入')])

而当我们的Fastapi框架中,涉及到定义的时候,则没对应的字段信息进行定义,比如如下一些定义:

class AuthAccountPasswordLoginParam(SchemaBase):
    '''
    账号密码登录参数
    '''
    account: str = Field(..., title="账号", description='手机号码不能为空')
    password: str = Field(..., title="密码", description='手机号码不能为空')
    validCode: str = Field(..., title="验证码", description='验证码不能为空')
    validCodeReqNo: str = Field(None, title="验证码请求号", description='')
    device: str = Field(None, title="设备信息", description='')

或者是:


class UserModel(SchemaBase):
    name: str = Field(..., title="手机号")
    age: int = None
    sex: str = 'male'
    address: str = None
    mobile: str = None

当Fastapi出现错误的时候,我们则无法定义上述类似的错误提示文本信息。对于有强迫感的人来说,还是有点不爽。

pydantic自定义中文错误模板

1. 问题表现

对于Fastapi框架不管是GET还是POST中的参数,我个人的习惯还是喜欢定义为一个模型来传入,如下代码所示:

定义参数模型:

class SchemaBase(BaseSchema):
    pass
class UserModel(SchemaBase):
    name: str = Field(..., title="手机号")
    age: int = None
    sex: str = 'male'
    address: str = None
    mobile: str = None

对于如果是查询参数来说,比较棘手的一个问题是UserModel需要做一层依赖转换它还会转为查询参数,如下接口定义所示:

@RestParameter(modelss=UserModel)
@get("/b/doLogin2ceshi/", summary='执行登入接口')
async def doLoginceshi(self,suemolde:UserModel =Depends(UserModel)):
    # TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
    return "AAAAAAAAAAAA"


@RestParameter(modelss=UserModel)
@post("/b/doLogin2ceshi2222/", summary='执行登入接口')
async def doLoginceshi22222(self,suemolde:UserModel):
    # TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
    return "AAAAAAAAAAAA"

上面代码中我们注意到在get中,我们对suemolde:UserModel =Depends(UserModel))做了一层转换,这样才可以实现模型转化为查询参数转化的处理。

基于上面的模型定义中,如果我们的不管是在get还是在post中如果没有传入“name”参数的情况下,都会返回对应的错误提示信息,如下是我们的全局拦截错误处理图示:

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

而当我们的没有传入“name”参数则会抛出如下图所示错误提示,:

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

文本提示如下所示:

{
	"message": "参数校验错误,请检查提交的参数信息",
	"code": 10031,
	"success": false,
	"result": {
		"message": "Validation error.",
		"fields": [{
			"name": "asas",
			"message": "Value is not a valid integer."
		}],
		"errors": [{
			"location": "query -> asas",
			"message": "value is not a valid integer",
			"error_type": "type_error.integer"
		}]
	},
	"timestamp": 1672809002591
}

总体来说,我们对此的错误提示信息还是有点不爽,因为不够干净。更多的时候我们是喜欢对特定的字段,特定的错误,抛出特定的错误模板文本信息,如下图所示:

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

2.关键解决思路

其实解决问题思路一开始我是打算通过反射获取对应的抛出异常的时候对应的模型的对象进行映射,我们的错误模本的定制,如下图所示: FastApi(自用脚手架)+Snowy搭建后台管理系统(9)-自定义RequestValidationError参数校验中文错误提示

但是对应的exc: Union[RequestValidationError, ValidationError]只会在POST的情况下携带我们的当前请求的对应的模型对象,对于GET转为为查询参数后的模型则无从知晓,所以我们需要想方设法给我们的当前请求处理异常的透传一个当前对应的查询的参数模型对象。然后通过对应的请求上下文获取当前对应的模型对象。所以我们需要定义个装饰器,用于给当前路由透传一个模型,我用到思路就是结合依赖注入和请求上下文设置一个当前模型参数。如下代码所示:

def RestParameter(modelss:Type[MODEL_SCHEMA]):
    def call_func(fun: AnyCallable) -> Callable[[AnyCallable], AnyCallable]:
        if hasattr(fun, '_route_args'):
            route_args: ApiRouteArgs = getattr(fun, '_route_args')
            def depends_set_model_(request: Request):
                request.state.parameter_model = modelss
                return request
            parameter: Optional[params.Depends] = Depends(depends_set_model_)
            route_args.dependencies = list(route_args.dependencies).append(parameter) if route_args.dependencies else [ Depends(depends_set_model_)]
        return fun
    return call_func

上面的装饰器依赖于我们的当前一个路由参数参数对象的封装,ApiRouteArgs,它本质就是对我们的要添加的端点函数对象(路由函数)添加对应的参数,其中包括一个依赖注入项的"dependencies"列表。通过在路由参数(路由路径参数中的依赖项)依赖项实现“parameter_model”参数的透传。

定义模型错误模板信息

完成透传之后,我们可以根据错误拦截的时候传递的requets来获取到当前注入的模型对象,而这个模型对象是我们的实现定制的关键,如下模型类代码所示:

MODEL_SCHEMA = TypeVar("MODEL_SCHEMA", bound=SchemaBase)
class UserModel(SchemaBase):
    name: str = Field(..., title="姓名",min_length=6)
    age: int = None
    sex: str = 'male'
    address: str = None
    mobile: str = None

    class Config:
        error_msg = {
            # error_type根据定义的错误类型抛出之定义的错误信息
            'name': {
                'value_error.missing': '姓名不能为空',
                'value_error.any_str.min_length': '字符串长度至少6位以上'
            }
        }

首先我们在Config中自定义实现了一个error_msg定制的错误模板,其中对应的“name”对于我们可能需要实现的校验错误定制了错误提示信息,比如:

  • 必传验证错误的时候(value_error.missing),则提示姓名不能为空
  • 如果参数长度最小不够的时候(value_error.any_str.min_length),则提示字符串长度至少6位以上

我们需要做就是在抛出错误的时候需要获取关键信息有:

  • 错误的字段信息
  • 错误字段类型

最终我们的错误的拦截处理如下图示:

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

如此我们的就完成了对应的自定义RequestValidationError参数校验中文错误提示,更多的错误类型则,我们需要针对性进行定义了!!最终效果图示如下:

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

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

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

结尾

END

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

掘金:juejin.cn/user/296393…

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

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

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