在开发Python的后端API平台的时候,为了兼容我SqlSugar开发的一些Winform端、BS端、UniApp端、WPF端等接入,由于部分是基于.net的处理,因此可能对于接入对象的属性为常见的Camel的驼峰命名规则,但是Python一般约定属性名称为小写,因此需要对这个模型进行兼容;另外默认FastAPI路由路径也是大小写敏感的,因此也需要做兼容处理,本篇随笔介绍使用FastAPI处理数据输入的时候,对模型数据和路径参数的一些转换处理。
1、默认Pydantic的大小处理
在 Pydantic 中,model_validate
方法用于验证和创建模型实例,并且默认情况下是大小写敏感的。也就是说,JSON 数据中的字段名需要与模型中的字段名完全匹配,包括大小写。
Pydantic 默认不支持直接取消字段名的大小写敏感性。为了处理字段名的大小写敏感问题,我们需要另外处理,有几种方式进行实现。
1)预处理 JSON 数据
在传递 JSON 数据到 model_validate
之前,手动将 JSON 数据中的字段名转换为模型所需的格式(例如,全部小写或全部大写)。
2)使用自定义字段别名
在 Pydantic 模型中使用字段别名来处理不同的字段名称。这种方法适用于字段名有明确且一致的变化情况(例如,使用不同的大小写风格)。
3)使用自定义数据解析
如果需要更复杂的字段名处理,你可以实现自定义解析逻辑。例如,通过编写一个函数来将数据字段名标准化为所需的格式。
最后这种方式相对比较好,不过每次都要求进行一个函数的转换,着实不太方便,万一忘记了呢?所以我希望使用一个没有显著调用过程的实现,隐式的处理方式,也就是使用使用model_validator进行隐式的转换处理。
model_validator
是 Pydantic v2 中用于模型验证的功能。要使用 model_validator
来处理字段名大小写不敏感的问题,你需要在模型中实现自定义的验证逻辑,以将字段名标准化为一致的格式(如小写)。
以下是如何使用 model_validator
处理字段名大小写不敏感的示例:
详细说明
- 模型定义:定义一个继承自
BaseModel
的 Pydantic 模型,如MyModel
。 - 使用
model_validator
:使用@model_validator
装饰器定义一个自定义的验证方法。在这个方法中,你可以将字段名转换为小写,以处理大小写不敏感的问题。
-
mode='before'
:指定在模型创建之前执行此验证器。 -
values
参数是一个字典,包含所有传入的数据字段。 -
normalized_values
字典用于存储转换后的字段名和值。
- 创建模型实例:在 FastAPI 路由处理函数中,使用
MyModel.model_validate(data)
创建模型实例。这里data
是原始的 JSON 数据,经过model_validator
处理后,字段名会被标准化为小写。
但是这样对于获得数据库对象,并转换为DTO对象(或者Schema对象)的时候,会导致模型转换出现问题,如下FastAPI的处理出现问题。
主要原因是模型对象转换为dict类型的时候出现错误,因此需要限定转换的对象为dict类型,修改下基类的模型处理如下所示。
通过Python的继承关系处理,我们所有子类对象,都可以实现查询参数的无感的小写转换,而不影响数据库对象的转换。
转换注意:
在 Pydantic v2 中,ConfigDict
是用于配置 Pydantic 模型行为的一个机制。str_to_lower
配置项用于将输入字符串转换为小写,但它主要适用于字符串类型字段的值,而不是字段名。
如果你需要实现模型字段名的大小写不敏感,你可以使用 model_validator
进行自定义处理。
另外,如果仅仅单独使用对request.query_params的键转换小写,那么在Post请求获得的Body内容,无法进行大小写转换的,而且可能触发Body内容提前被消耗而导致再次读取的时候错误,但是使用model_validator
进行自定义处理则是可以的,因此model_validator
是比较推荐的处理方式。
2、对路由路径大小写转换处理
在 FastAPI 中,定义路由路径时,路径是大小写敏感的。这意味着 /items/
和 /Items/
被视为两个不同的路径。如果你希望路由路径不区分大小写,需要在代码中进行自定义处理,因为 FastAPI 不原生支持这一特性。
在上面的示例中,访问 /items/
和 /Items/
会触发不同的路由处理函数。
如果你希望所有路由路径都不区分大小写,可以使用中间件来实现。例如,可以编写一个中间件,将请求路径转换为小写。
在这个例子中,无论是 /items/
、/Items/
还是 /ITEMS/
,都将触发 read_items
函数,因为路径在中间件中被转换为小写。
这种实现方式会导致所有路由都不区分大小写,因此在设计路由时要考虑是否需要保持路径的区分。
3、在FastAPI的控制器处理中,提示获取不到request.user的值?
在 FastAPI 中,request.user
通常与身份验证系统相关,特别是在使用像 fastapi-users
或自定义认证中间件时。如果你在处理请求时无法获取 request.user
的值,可能有以下几个原因:
1)确保身份验证依赖项正确配置
request.user
通常依赖于身份验证依赖项或中间件。例如,如果你使用 OAuth2 或 JWT 验证,需要确保正确设置了依赖项以填充 request.user
。
例如我们在FastAPI的路由器中定义一个接口,我们要求该接口读取用户的身份信息(通过token获取身份信息)
其中DependsJwtAuth 就是要求通过Token验证的,否则提示权限不足,无法获得接口正常的数据。而它很简单的处理,如下代码
由于我们在用户登录授权生成访问Token的时候,会返回相关的用户身份信息。
也就是验证的时候,可以获得用户的对象信息了
因此获得当前用户身份的信息代码,就可以正常工作了。
确保请求包含正确的身份验证信息(如 Authorization
header)。如果缺少或不正确,request.user
可能无法被填充。
如果我们确认用户身份,可以直接获得相关的用户属性信息了(模型中包含fullname属性等)。
这样我们可以通过中间件的方式,把用户身份信息提取出来,进行访问的日志的记录用途了。
我们在很多接口里面,都需要用户进行登录获取授权令牌,并设置请求头来确认令牌信息,才能进行下一步的操作接口,也就是FastAPI 中自定义用户身份验证逻辑,需要继承 AuthenticationBackend
类并实现 authenticate
方法。
首先,需要安装 starlette
,因为 AuthenticationBackend
是 Starlette
框架的一部分,而 FastAPI 本身是基于 Starlette
的。
最后通过处理验证后,可以返回相关的验证信息和用户对象。
当然,我们也可以继承BaseUser来获得一些基础信息,返回这个用户对象信息。
你可以创建一个自定义的 AuthenticationBackend
子类,并实现 authenticate
方法。这个方法接收一个 Request
对象,并返回一个包含 AuthCredentials
和 BaseUser
的元组。
最后,将自定义的认证后端添加到 FastAPI 应用中。使用 app.add_middleware
方法将认证后端集成到应用中。
通过继承 AuthenticationBackend
,你可以在 FastAPI 中实现自定义的身份验证逻辑,并将其应用于整个应用程序。这样可以灵活地处理各种身份验证方案,如 JWT、OAuth、或自定义的认证方式。