FastAPI请求参数处理

引言

在FastAPI中,HTTP请求的参数获取和验证是非常重要的功能。本文将详细介绍FastAPI中几种常见的请求参数形式,以及它们的获取方式和验证机制。

请求参数的常见形式

一个典型的RESTful API中,请求参数主要有以下几种形式:

  • 查询字符串参数-Query Parameters:URL中?号后面的查询参数,例如?query=test
  • 路径参数-Path Parameters:URL路径中的参数,例如/path/{param}
  • 请求体-Request Body:请求体中的JSON格式数据
  • 请求头-Headers:请求头中的参数
  • 文件上传-Files
  • 其他的请求信息-Request

下面我将依次对这几种参数的处理进行说明。

请求参数处理Demo

python 复制代码
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 请求参数案例 }
# @Date: 2023/11/06 11:15
import uvicorn
from fastapi import FastAPI, Header, UploadFile
from pydantic import BaseModel, Field

app = FastAPI(summary="请求参数案例")


@app.get("/query_params", summary="查询字符串参数Demo")
def with_query_params(name: str, age: int):
    return {"name": name, "age": age}


@app.get("/user/{user_id}/path_params", summary="路径参数Demo")
def with_path_params(user_id: int):
    return {"user_id": user_id}


class UserLoginIn(BaseModel):
    account: str = Field(..., description="账号")
    password: str = Field(..., description="密码")


@app.post("/user/login/json_params", summary="json参数demo")
def with_json_params(req_model: UserLoginIn):
    return req_model.model_dump()


@app.get("/user/detail/header_params", summary="请求头参数demo")
def with_header_params(
        token: str = Header(description="访问token"),
        user_agent: str = Header(description="用户代理")
):
    return {"token": token, "User-Agent": user_agent}


@app.post("/file_upload/file_params", summary="文件参数demo")
async def with_file_params(file: UploadFile):
    return {"filename": file.filename, "file_size": file.size}


@app.get("/users/{user_id}/path_query_params", summary="路径参数+查询字符串参数demo")
def with_path_query_params(user_id: int, age: int = None):
    return {"user_id": user_id, "age": age}


def main():
    uvicorn.run(app)


if __name__ == '__main__':
    main()

请求处理注意点

这里需要注意,如下几点

  1. 请求头参数需要从 fastapi 导入 Header 来标识是请求头的参数

    1. http固定的请求头参数,fastapi处理是要用小写,然后中划线,改成下划线,如下

      1. Content-Type => content_type
      2. User-Agent => user_agent
  2. 处理文件参数有点特殊,也是需要从 fastapi 导入 UploadFile 进行处理,这个类帮忙封装了一些文件信息

    1. 然后还需要下载一个 python-multipart 依赖以支持多媒体传输
  3. 请求体使用json格式交互用了pydantic的BaseModel 可以写参数的描述信息,所以在API文档中有中文的描述,而路径、查询字符串参数没有。需要使用fastapi的Path、Query对象才有。

    1. 如果希望get请求的路径、查询字符串参数也可以像body一样使用 pydantic的BaseModel维护,需要借助Depends、 Path、Query对象,但会出现description信息不会显示在API文档中,需要解决则还要再BaseModel类中使用Field与Query等对象嵌套来解决。

针对第3点,我写个Demo展示下如何处理

python 复制代码
class UserQueryIn(BaseModel):
    user_id: int = Path(description="用户ID")
    name: Optional[str] = Query(default=None, description="姓名")
    age: Optional[int] = Query(default=None, description="年龄")


@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn):
    # 业务逻辑处理
    # logic_func(req_model)
    return req_model.model_dump()

如果直接用BaseModel 的话 fastapi 会认为是body参数,这明显不合理是错误,因此需要通过 fastapi的Depends函数来解决。

python 复制代码
class UserQueryIn(BaseModel):
    user_id: int = Path(description="用户ID")
    name: Optional[str] = Query(default=None, description="姓名")
    age: Optional[int] = Query(default=None, description="年龄")


@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
    # 业务逻辑处理
    # logic_func(req_model)
    return req_model.model_dump()

但会出现description信息不展示,如果需要把API文档导入到APIFOX中的话没有中文描述还是不太好,因此还可以借助在BaseModel中让Field与Query等对象嵌套来解决。

python 复制代码
class UserQueryIn(BaseModel):
    user_id: int = Field(Path(description="用户ID"))
    name: Optional[str] = Field(Query(default=None, description="姓名"))
    age: Optional[int] = Field(Query(default=None, description="年龄"))


@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
    # 业务逻辑处理
    # logic_func(req_model)
    return req_model.model_dump()

为了统一使用pydantic的BaseModel类处理请求参数,还是有点小坑,感觉get请求不太好使用BaseModel,希望FastAPI后续能优化下。

为什么要统一使用BaseModel呢?看看如下例子

python 复制代码
@app.get("/users/{user_id}/path_query_params", summary="路径参数+查询字符串参数demo")
def with_path_query_params(
        user_id: int = Path(description="用户ID"),
        age: int = Query(default=None, description="年龄查询")
):
    # 业务逻辑处理
    # logic_func(user_id=user_id, age=age)
    return {"user_id": user_id, "age": age}



class UserQueryIn(BaseModel):
    user_id: int = Field(Path(gt=0, description="用户ID"))
    name: Optional[str] = Field(Query(default=None, min_length=1, description="姓名"))
    age: Optional[int] = Field(Query(default=None, gt=0, description="年龄"))


@app.get("/users/{user_id}/path_query_params2", summary="路径参数+查询字符串参数BaseModel的demo")
def with_path_query_params(req_model: UserQueryIn = Depends(UserQueryIn)):
    # 业务逻辑处理
    # logic_func(req_model)
    return req_model.model_dump()

如果查询参数需要改动变化、只需要在BaseModel类中维护就好,后面还可以添加校验器,整体的路由函数更简洁不会一堆的参数,传递给逻辑层处理只要透传一个请求模型 req_model 就可以了,可以说更统一规范、更好维护。麻烦就是要在定义的时候使用Depends与Query等。

请求对象

基本请求信息都可以声明参数获取,但一些额外的请求信息,例如请求的IP、Method、URL等则需要通过Request对象进行获取,具体使用如下:

python 复制代码
from fastapi import FastAPI, Request


@app.get("/request_obj", summary="请求对象的demo")
def req_obj_demo(req: Request):
    print("req client ip", req.client.host)
    print("req method", req.method)
    print("req base_url", req.base_url)
    print("req url", req.url)

    print("Request", req)
    return {
        "client_ip": req.client.host,
        "method": req.method,
        "base_url": req.base_url,
        "url": req.url,
    }

Ret

csharp 复制代码
req client ip 127.0.0.1
req method GET
req base_url http://127.0.0.1:8000/
req url http://127.0.0.1:8000/request_obj
Request <starlette.requests.Request object at 0x104bf8af0>

这个Request对象,再每个路由函数中都存在只要声明的参数类型是FastAPI的Request即可,就可以从中获取请求参数、以及一些其他的请求的信息。这个Request对象一般在fastapi的中间件中使用多,后续再介绍。

总结

通过上面的示例可以看出,FastAPI通过声明参数、Depends注入、Pydantic模型验证等方式,提供了非常便捷的请求参数处理机制。各种参数形式也都得到了很好的支持。使用pydantic的BaseModel传递组织参数,相比其他框架使用字典来组织,代码更清晰、更好维护。

源代码

Github:github.com/HuiDBK/Fast...

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
默_笙3 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
qq_426003963 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
长沙三为智能科技3 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端