FastApi(2)-参数接收和验证

1.参数接收

1.1 路径参数(不推荐)

1.代码清单

app/router下,新增demo_router.py文件,内容如下:

from fastapi import APIRouter

router = APIRouter(
    prefix="/demo",
    tags=["演示接口"]
)

@router.get("/path/{order_id}")
async def pathParamReceive(order_id: int):
    """
    路径参数接收演示
    """
    return {
        "接受结果": order_id,
    }

@注意:当我们定义参数类型时,FastAPI 接受参数时,会自动进行"解析",如果类型不一致,则会报错。

2.请求验证

# 正常传参
➜  curl http://127.0.0.1:8000/demo/path/12222
{"接受结果":12222}
# 当传参类型不匹配时,接口定义是:int
➜  curl http://127.0.0.1:8000/demo/path/hello
{"detail":[{"loc":["path","order_id"],"msg":"value is not a valid integer","type":"type_error.integer"}]}
# 当什么都不传时
➜ curl http://127.0.0.1:8000/demo/path/
{"detail":"Not Found"}

3.顺序大坑

假如我们定义两个接口,带路径参数的:/path/{order_id}和不带路径参数的:/path/test,可能会因为顺序问题,导致我们无法正常访问/path/test,例如路由注册时代码如下:

...
@router.get("/path/{order_id}")
async def pathParamReceive(order_id: int):
    """
    路径参数接收-演示-带路径参数
    """
    return {
        "接受结果": order_id,
    }

@router.get("/path/test")
async def pathParamReceive2():
    """
    路径参数接收-演示-不带路径参数
    """
    return {
        "msg": "hello",
    }

然后进行访问:

# 带路径参数的可以正常访问
➜  curl http://127.0.0.1:8000/demo/path/99999
{"接受结果":99999}
# 不带路径参数的可以正常访问
➜ curl http://127.0.0.1:8000/demo/path/test
{"detail":[{"loc":["path","order_id"],"msg":"value is not a valid integer","type":"type_error.integer"}]}

通过替换两个函数位置,就可以正常访问了, 感觉比较奇葩,不推荐使用,只做了解即可。更多使用方法可见官方文档: https://fastapi.tiangolo.com/zh/tutorial/path-params/

1.2 查询参数

1.代码清单

app/router下,新增demo_router.py文件,内容如下:

from typing import Union
...
@router.get("/query/receive")
async def queryParamReceive(username: str, sex: str = '男', city: Union[str, None] = None):
    """
    查询参数接受-演示
    """
    return {
        "msg": "查询参数接收",
        "result": {
            "username": username,
            "sex": sex,
            "city": city,
        }
    }
...

2.参数约束

  • username: str: 代表参数username为必填字段;
  • sex: str = '男': 代表参数sex为选填字段,并且有默认值;
  • city: Union[str, None] = None: 代表参数city为选填字段,并无默认值; Uniontyping 模块中的一个泛型类,用于表示多个类型中的一个;

@注意: 当参数有默认值时,顺序一定要放在没有默认值参数后面,否则会提示语法错误:SyntaxError: non-default argument follows default argument

3.请求验证

1.3 请求体(推荐)

使用请求体接受参数,一般分为两个步骤:

  • 第一步: 使用Pydantic模型声明一个请求体(其实就是class);
  • 第二步: 路由函数的参数绑定上这个模型;

@注意:工作中比较常用的参数接收方式,推荐使用~

1.定义模型

文件位置: app/parameter/demo_param.py

from typing import Union
# 导入pydantic对应的模型基类
from pydantic import BaseModel

class DemoParam(BaseModel):
    """
    请求体参数对应的模型
    """
    user_name: str
    age: int
    city: Union[str, None]

2.优化导入

文件位置: app/parameter/__init__.py

from app.parameter.demo_param import DemoParam

3.使用

文件位置: app/router/demo_router.py

# from app.parameter.demo_param import DemoParam 如果没有优化导入,需要用这种方式导入
from app.parameter import DemoParam # 如果没有优化导入,这行会报错

router = APIRouter(
    prefix="/demo",
    tags=["演示接口"]
)

@router.post("/query/receive")
async def bodyReceive(body: DemoParam):
    """
    请求体参数接受-演示
    """
    return {
        "msg": "请求体参数接受",
        "result": {
            "body": body,
        }
    }

4.验证

# 必填参数user_name,不传时
curl -X 'POST' \
  'http://127.0.0.1:8000/demo/query/body/receive' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "age": 99,
  "city": "北京"
}'

{"detail":[{"loc":["body","user_name"],"msg":"field required","type":"value_error.missing"}]}

# 必填参数user_name,传值时
➜ curl -X 'POST' \
  'http://127.0.0.1:8000/demo/query/body/receive' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "user_name": "娃哈哈",
  "age": 99,
  "city": "北京"
}'
{"msg":"请求体参数接受","result":{"body":{"user_name":"娃哈哈","age":99,"city":"北京"}}}

# 必填参数user_name,传空时
➜  curl -X 'POST' \
  'http://127.0.0.1:8000/demo/query/body/receive' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "user_name": "",
  "age": 99,
  "city": "北京"
}'
{"msg":"请求体参数接受","result":{"body":{"user_name":"","age":99,"city":"北京"}}}

2.参数验证

Python中推荐使用成熟的第三方库进行数据验证,这样不仅可以少写一些if .. elif..,还能让代码的可读性更强;

2.1 Pydantic介绍

Pydantic 是一个 Python 库,用于数据验证和设置,特别是用于验证数据模型。它通过声明性的方式定义数据模型,并提供了强大的数据验证和转换功能。Pydantic 最初是为 FastAPI 框架设计的,但它也可以在其他 Python 项目中独立使用。

@注意: 文中使用的pydantic版本为: 1.10.11, 貌似不同的版本,可能会存在差异;具体可参见官方文档: https://docs.pydantic.dev/latest/

使用Pydantic 的本质,其实就是如何编写对应的数据验证规则,下面列举一些常用的规则:

2.2 常用验证

下面列举一些常用的验证规则:

  • 基本数据类型: int, float, str, bool;
  • 可选参数: Optional[type] 表示可选参数,Union[x, None]也可以表示可选;
  • 整数范围: 结合conint函数判断数字范围 ,如age: conint(ge=18, le=30); ge:大于等于、gt:大于、le:小于等于、lt:小于
  • 字符长度: 结合constr函数判断字符长度,如: constr(min_length=6, max_length=10);
  • 正则表达式: 使用constr函数中的参数regex ,可以用于进行正则表达式验证;
  • 枚举验证: 使用Enum 定义枚举类,验证。
  • 列表类型: 使用List[type] 来限制列表值的类型,并尝试把参数转成对应的类型。
  • 字典类型: Dict[key_type, value_type] 来限制字典keyval类型,并尝试把参数转成对应的类型。
from enum import Enum
from typing import Union, Optional, List, Dict

# 导入pydantic对应的模型基类
from pydantic import BaseModel, constr, conint

class GenderEnum(str, Enum):
    """
    性别枚举
    """
    male = "男"
    female = "女"

class PydanticVerifyParam(BaseModel):
    """
    用来学习使用pydantic模型验证
    """
    user_name: str  # 基本类型
    age: conint(ge=18, le=30)  # 整数范围:18 <= age <= 30
    password: constr(min_length=6, max_length=10)  # 字符长度
    phone: constr(regex=r'^1\d{10}$')  # 正则验证手机号
    address: Optional[str] = None  # 可选参数
    sex: GenderEnum  # 枚举验证,只能传: 男和女
    likes: List[str]  # 值会自动转成传字符串列表
    scores: Dict[str, float]  # key会转成字符串,val 会转成浮点型

@注意:上面列举的都是基本使用,实际中可以组合进行多项组合使用,如items: List[constr(min_length=1, max_length=3)] : 限制列表中的每个字符串长度的范围

2.3 自定义验证

@validator 装饰器用于定义自定义验证函数,具体是如下:

from pydantic import BaseModel, constr, conint, validator
...
class PydanticVerifyParam(BaseModel):
    """
    用来学习使用pydantic模型验证
    """
    user_name: str  # 基本类型
    ...
    @validator("user_name")
    def validateUsername(cls, value: str):
        """
        自定义验证函数
        """
        if value.find("傻") > -1:
            raise ValueError("user_name不能包含敏感词")
        return value

验证结果:

// 当参数user_name传:你好傻
{
  "detail": [
    {
      "loc": [
        "body",
        "user_name"
      ],
      "msg": "user_name不能包含敏感词",
      "type": "value_error"
    }
  ]
}

2.4 其他验证

  • EmailStr: 用于验证字符串是否是有效的电子邮件地址。
  • IPvAnyAddress: 用于验证字符串是否是有效的 IPv4 或 IPv6 地址。
  • StrictBool:用于验证字符串是否是严格的布尔值(truefalse)。
  • AnyHttpUrl: 用于验证字符串是否是有效的 URL,包括以 httphttps 开头的URL

更多验证方式可查看文档: https://docs.pydantic.dev/latest/concepts/validators/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容