当前位置: 首页 > news >正文

深入FastAPI:路径参数、查询参数及其检校

引言

大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年11月学习赛的FastAPI学习总结文档;本文主要讲解路径参数、查询参数及其检校机制。💕💕😊


介绍

FastAPI 是一个现代、快速(高性能)的 Web 框架,基于 Python 3.7+ 标准类型提示。它特别适合构建 API,因为它支持异步编程、自动生成文档,并且具有强大的数据验证功能。本文将深入探讨 FastAPI 中的路径参数、查询参数及其检校机制,并通过实际代码示例帮助你更好地理解和应用这些概念


一、FastAPI 基础

1. FastAPI 简介

FastAPI 是一个基于 Python 的 Web 框架,旨在提供高性能、易于使用和快速开发的特性。它利用 Python 的类型提示来进行数据验证和自动生成文档,使得开发 API 变得更加简单和高效。

2. 安装 FastAPI

首先,你需要安装 FastAPI 和 Uvicorn(一个 ASGI 服务器)。你可以通过以下命令进行安装:

pip install fastapi uvicorn

3. 创建第一个 FastAPI 应用

创建一个简单的 FastAPI 应用,启动一个基本的 Web 服务:

from fastapi import FastAPIapp = FastAPI()@app.get("/")
def read_root():return {"Hello": "World"}if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)


二、路径参数

1. 常见 HTTP 请求方法

在 FastAPI 中,常见的 HTTP 请求方法包括:

① GET:用于获取资源
  • 幂等性:是
  • 缓存:GET 请求可以被缓存,这意味着如果相同的请求多次发出,浏览器可能会使用缓存的响应而不是重新发送请求。
  • 安全性:GET 请求是安全的,因为它只用于读取数据,不会对服务器上的资源进行修改。
  • URL 参数:GET 请求的参数通常通过 URL 传递,这使得它们容易被记录在服务器日志、浏览器历史记录中,并且可以被收藏为书签。
  • 使用场景:适用于获取数据、查询资源、获取资源列表等场景。
② POST:用于创建资源
  • 幂等性:否
  • 缓存:POST 请求不会被缓存,每次请求都会导致服务器处理请求并生成新的资源。
  • 安全性:POST 请求不是安全的,因为它会修改服务器上的资源。
  • 请求体:POST 请求的数据通常放在请求体中,这使得它可以传递更复杂的数据结构,如 JSON、XML 等。
  • 使用场景:适用于提交表单数据、上传文件、创建新资源等场景。
③ PUT:用于更新资源
  • 幂等性:是
  • 缓存:PUT 请求不会被缓存。
  • 安全性:PUT 请求不是安全的,因为它会修改服务器上的资源。
  • 请求体:PUT 请求的数据通常放在请求体中,用于更新指定的资源。如果资源不存在,PUT 请求可以用于创建资源。
  • 使用场景:适用于更新现有资源、替换资源内容等场景。
④ DELETE:用于删除资源
  • 幂等性:是
  • 缓存:DELETE 请求不会被缓存。
  • 安全性:DELETE 请求不是安全的,因为它会删除服务器上的资源。
  • 使用场景:适用于删除指定资源、清理资源等场景。
⑤ 补充知识
  • 幂等性与安全性:幂等性和安全性是 HTTP 方法的两个重要特性。幂等性意味着多次执行相同的操作不会产生不同的结果,而安全性意味着操作不会修改服务器上的资源。
  • URL 确定性:在使用 PUT 和 POST 时,URL 的确定性是一个关键因素。如果 URL 可以在客户端确定(例如,资源的唯一标识符已知),则使用 PUT;如果 URL 需要在服务端确定(例如,使用数据库自增主键),则使用 POST。
  • 状态码:在 FastAPI 中,不同的 HTTP 方法通常会返回不同的状态码。例如,GET 请求成功时通常返回 200 OK,POST 请求成功时通常返回 201 Created,PUT 请求成功时通常返回 200 OK 或 204 No Content,DELETE 请求成功时通常返回 204 No Content。

2. 路径参数的基本概念

路径参数是 URL 中的一部分,用于标识特定的资源。它们通常用于动态地指定资源的位置或标识符。路径参数在 FastAPI 中非常重要,因为它们允许你根据 URL 中的不同部分来处理不同的资源。

假设我们有一个 API,用于管理用户和他们的订单。我们可以使用路径参数来标识特定的用户和订单。

示例 1:获取特定用户的信息
from fastapi import FastAPIapp = FastAPI()@app.get("/users/{user_id}")
def get_user(user_id: int):# 假设我们从数据库中获取用户信息user_info = {"user_id": user_id, "name": "John Doe", "email": "john.doe@example.com"}return user_info

在这个例子中,/users/{user_id} 中的 {user_id} 是一个路径参数。当客户端请求 /users/123 时,user_id 将被解析为 123,并且 get_user 函数将返回用户 ID 为 123 的用户信息。如下图:

示例 2:获取特定用户的订单
@app.get("/users/{user_id}/orders/{order_id}")
def get_order(user_id: int, order_id: int):# 假设我们从数据库中获取订单信息order_info = {"user_id": user_id, "order_id": order_id, "product": "Laptop", "price": 1200}return order_info

在这个例子中,/users/{user_id}/orders/{order_id} 中的 {user_id}{order_id} 是两个路径参数。当客户端请求 /users/123/orders/456 时,user_id 将被解析为 123order_id 将被解析为 456,并且 get_order 函数将返回用户 ID 为 123 的订单 ID 为 456 的订单信息。

示例 3:更新特定用户的订单
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()@app.put("/users/{user_id}/orders/{order_id}")
def update_order(user_id: int, order_id: int, new_product: str, new_price: float):# 假设我们更新数据库中的订单信息if not order_exists(user_id, order_id):raise HTTPException(status_code=404, detail="Order not found")# 更新订单信息update_order_in_db(user_id, order_id, new_product, new_price)return {"message": "Order updated successfully"}def order_exists(user_id: int, order_id: int) -> bool:# 假设我们检查订单是否存在return True  # 示例中假设订单总是存在def update_order_in_db(user_id: int, order_id: int, new_product: str, new_price: float):# 假设我们更新数据库中的订单信息pass

在这个例子中,/users/{user_id}/orders/{order_id} 中的 {user_id}{order_id} 是两个路径参数。当客户端请求 /users/123/orders/456 并附带新的产品名称和价格时,update_order 函数将更新用户 ID 为 123 的订单 ID 为 456 的订单信息。

完整代码
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()# 示例 1:获取特定用户的信息
@app.get("/users/{user_id}")
def get_user(user_id: int):# 假设我们从数据库中获取用户信息user_info = {"user_id": user_id, "name": "John Doe", "email": "john.doe@example.com"}return user_info# 示例 2:获取特定用户的订单
@app.get("/users/{user_id}/orders/{order_id}")
def get_order(user_id: int, order_id: int):# 假设我们从数据库中获取订单信息order_info = {"user_id": user_id, "order_id": order_id, "product": "Laptop", "price": 1200}return order_info# 示例 3:更新特定用户的订单
@app.put("/users/{user_id}/orders/{order_id}")
def update_order(user_id: int, order_id: int, new_product: str, new_price: float):# 假设我们更新数据库中的订单信息if not order_exists(user_id, order_id):raise HTTPException(status_code=404, detail="Order not found")# 更新订单信息update_order_in_db(user_id, order_id, new_product, new_price)return {"message": "Order updated successfully"}def order_exists(user_id: int, order_id: int) -> bool:# 假设我们检查订单是否存在return True  # 示例中假设订单总是存在def update_order_in_db(user_id: int, order_id: int, new_product: str, new_price: float):# 假设我们更新数据库中的订单信息pass# 启动 FastAPI 应用程序的指令
if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)

路径参数在 FastAPI 中用于动态地指定资源的位置或标识符。通过在 URL 中使用路径参数,你可以根据不同的参数值来处理不同的资源。路径参数的使用使得 API 更加灵活和强大,能够处理各种复杂的资源请求。

3. 构建一个 TodoList 的 CRUD 示例

我们以一个简单的 TodoList 应用为例,展示如何使用 FastAPI 进行增删改查操作:

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Listapp = FastAPI()class Todo(BaseModel):id: inttask: strtodos = [{"id": 1,"task": "Learn FastAPI"
}]@app.post("/todos/", response_model=Todo)
def create_todo(todo: Todo):todos.append(todo)return todo@app.get("/todos/", response_model=List[Todo])
def read_todos():return todos@app.get("/todos/{todo_id}", response_model=Todo)
def read_todo(todo_id: int):for todo in todos:print(todo),"todo"if todo["id"] == todo_id:return todoreturn {"error": "Todo not found"}@app.put("/todos/{todo_id}", response_model=Todo)
def update_todo(todo_id: int, updated_todo: Todo):for i, todo in enumerate(todos):if todo["id"] == todo_id:todos[i] = updated_todoreturn updated_todoreturn {"error": "Todo not found"}@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):for i, todo in enumerate(todos):if todo["id"]== todo_id:del todos[i]return {"message": "Todo deleted"}return {"error": "Todo not found"}# 启动 FastAPI 应用程序的指令
if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)

这里我们可以使用FastAPI自带的文档http://127.0.0.1:8000/docs进行查看:

  • GET请求

  • POST请求

  • PUT请求

  • DELETE请求

4. 路径参数的类型限制

在 FastAPI 中,路径参数可以指定类型,例如 intstr 等。FastAPI 会自动进行类型转换和验证。

@app.get("/items/{item_id}")
def read_item(item_id: int):return {"item_id": item_id}

如果使用了不同的类型,则会报错

5. 数据检校与 Pydantic

Pydantic 是 FastAPI 中用于数据验证和解析的库。它通过 Python 的类型提示来定义数据模型,并自动进行验证。Pydantic 的主要作用是确保传入的数据符合预期的格式和类型,从而减少手动验证的复杂性和错误。

from pydantic import BaseModelclass Item(BaseModel):name: strprice: floatis_offer: bool = None

在这个案例中,我们定义了一个 Item 类,它继承自 BaseModel。这个类有三个字段:

  • name: 类型为 str,表示商品的名称。
  • price: 类型为 float,表示商品的价格。
  • is_offer: 类型为 bool,表示商品是否为特价商品。这个字段有一个默认值 None,表示它是可选的。

使用 Pydantic 进行数据检校的一般流程如下:

  1. 定义数据模型:首先,你需要定义一个继承自 BaseModel 的类,并在类中定义字段及其类型。
  2. 实例化数据模型:当你接收到数据时,将数据传递给数据模型的构造函数,Pydantic 会自动验证数据是否符合定义的类型和格式。
  3. 处理验证结果:如果数据验证通过,你可以继续处理数据;如果验证失败,Pydantic 会抛出一个 ValidationError 异常,并提供详细的错误信息。

下面案例代码中,展示了如何使用 Pydantic 进行数据验证和解析。

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, ValidationErrorapp = FastAPI()# 定义数据模型
class Item(BaseModel):name: strprice: floatis_offer: bool = None# 示例 1:创建新商品
@app.post("/items/")
def create_item(item: Item):# 数据已经通过 Pydantic 验证return {"message": "Item created successfully", "item": item}# 示例 2:更新商品信息
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):# 假设我们更新数据库中的商品信息if not item_exists(item_id):raise HTTPException(status_code=404, detail="Item not found")# 更新商品信息update_item_in_db(item_id, item)return {"message": "Item updated successfully", "item": item}def item_exists(item_id: int) -> bool:# 假设我们检查商品是否存在return True  # 示例中假设商品总是存在def update_item_in_db(item_id: int, item: Item):# 假设我们更新数据库中的商品信息pass# 启动 FastAPI 应用程序的指令
if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)
  • 如果传入的数据符合 Item 模型的定义,Pydantic 会自动解析并验证数据。
  • 如果数据不符合定义,Pydantic 会抛出一个 ValidationError 异常,并返回详细的错误信息。

Pydantic 是 FastAPI 中用于数据验证和解析的强大工具。通过定义数据模型并使用 Pydantic 进行验证,开发者可以确保传入的数据符合预期的格式和类型,从而减少手动验证的复杂性和错误。Pydantic 还提供了详细的错误信息,辅助快速定位和修复问题。

6. 预设值与枚举值

你可以为路径参数设置预设值或使用枚举值进行限制:

from enum import Enumclass ModelName(str, Enum):alexnet = "alexnet"resnet = "resnet"lenet = "lenet"@app.get("/models/{model_name}")
def get_model(model_name: ModelName):if model_name == ModelName.alexnet:return {"model_name": model_name, "message": "Deep Learning FTW!"}if model_name.value == "lenet":return {"model_name": model_name, "message": "LeCNN all the images"}return {"model_name": model_name, "message": "Have some residuals"}

7. 包含路径的路径参数

路径参数可以包含路径,例如 /files/{file_path:path},其中 file_path 可以是一个文件路径。

@app.get("/files/{file_path:path}")
def read_file(file_path: str):return {"file_path": file_path}

三、查询参数

1. 查询参数的基本概念

查询参数是 URL 中 ? 后面的部分,用于传递额外的信息。例如,/items/?skip=0&limit=10 中的 skiplimit 就是查询参数。

2. 默认查询参数

你可以为查询参数设置默认值:

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):return {"skip": skip, "limit": limit}

这里的skip: int = 0就指代默认的skip参数是0,如果用户不指定该值,则会默认这个参数为0;

3. 必需查询参数

某些查询参数是必需的,如果没有提供,FastAPI 会返回错误:

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str):return {"item_id": item_id, "q": q}

这里我们没有为其设置默认值,如果不输入该值,其会报错

4. 可选查询参数

你可以将查询参数设置为可选的,并提供默认值:

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):return {"item_id": item_id, "q": q}

可有可没有的参数,如果有需要你就传入,如果不需要,函数也不会报错;注意区分可选查询参数和默认查询参数

5. 多个路径参数与多个查询参数

在 FastAPI 中,用户可以同时使用多个路径参数和多个查询参数:

@app.get("/items/{item_id}/users/{user_id}")
def read_item_user(item_id: int, user_id: int, q: str = None, short: bool = False):item = {"item_id": item_id, "user_id": user_id}if q:item.update({"q": q})if not short:item.update({"description": "This is an amazing item that has a long description"})return item
  • 路径参数用以简单参数传递;
  • 查询参数通过get或post请求中使用较多;

四、路径参数检校

在 FastAPI 中,路径参数校验是一个非常重要的功能,它允许开发者为路径参数添加类型检查、数值范围限制以及元数据声明。通过使用 Path 库可以轻松地实现这些功能。

1. 概念理解

① 导入 Path

首先,你需要从 fastapi 中导入 Path

from fastapi import FastAPI, Path
② 声明元数据

声明元数据是为了提供更多的信息,帮助开发者和其他使用者更好地理解 API 的设计意图。元数据可以包括标题、描述等。

from typing import Annotated
from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(title="The ID of the item to get")]
):results = {"item_id": item_id}return results

在这个例子中,item_id 是一个路径参数,并且我们为其添加了一个标题 "The ID of the item to get"。这个标题可以帮助其他开发者理解这个参数的用途。

③ 数值校验

数值校验是为了确保路径参数的值在合理的范围内。FastAPI 提供了以下几种数值校验参数:

  • gt: 大于(greater than)
  • ge: 大于等于(greater than or equal)
  • lt: 小于(less than)
  • le: 小于等于(less than or equal)

2.案例

① 大于等于校验

下面代码中展示了如何使用 Path 库进行数值校验:

import uvicorn
from typing import Annotated
from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(title="The ID of the item to get", ge=5)]
):results = {"item_id": item_id}return resultsif __name__ == '__main__':uvicorn.run(app, host='127.0.0.1', port=8009)

在这里,item_id 必须是一个大于或等于 5 的整数。如果请求的路径参数不满足这个条件,FastAPI 会返回一个 422 错误。

  • 测试

我们在http//127.0.0.1:8009/docs进行测试:

  • 访问 http://127.0.0.1:8009/items/6,你会看到 {"item_id": 6}

  • 访问 http://127.0.0.1:8009/items/4,你会看到一个错误信息,提示 item_id 必须大于或等于 5。

② 浮点数校验

数值校验同样适用于浮点数。以下是一个示例,展示了如何对浮点数进行校验:

import uvicorn
from typing import Annotated
from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items-float/{item_id}")
async def read_items(item_id: Annotated[float, Path(title="The ID of the item to get", ge=5.5)]
):results = {"item_id": item_id}return resultsif __name__ == '__main__':uvicorn.run(app, host='127.0.0.1', port=8009)

在这个例子中,item_id 必须是一个大于或等于 5.5 的浮点数。

  • 访问 http://127.0.0.1:8009/items/5.6,你会看到 {"item_id": 5.6}

  • 访问 http://127.0.0.1:8009/items/5.4,你会看到一个错误信息,提示 item_id 必须大于或等于 5.5。

这里你或许会尝试输入6来验证错误,但是在python中,整数是可以被自动转换为浮点型的,因此输入6并不会报类型错误;


五、查询参数检校

我们依旧在http://127.0.0.1:8000/docs#/中进行测试:

1. 字符串参数限制

你可以对查询参数进行字符串长度限制:

from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
def read_items(q: str = Query(None, max_length=50)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

这里如果传入的字符串长度超过50,就会报错!

2. 增加额外的约束条件

你可以为查询参数增加更多的约束条件,例如最小长度、正则表达式等:

@app.get("/items_with_constraints/")
def read_items_with_constraints(q: str = Query(None, min_length=3, max_length=50, regex="^[a-z]+$")
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

这里要求我们最小长度为3,最大为50;q只能包含小写字幕并且至少有一个字符;这里如果我们填写不规范,都会报错422;

3. 最短字符长度与正则表达式

你可以使用 min_lengthregex 来进一步限制查询参数:

@app.get("/items_with_regex/")
def read_items_with_regex(q: str = Query(None, min_length=3, max_length=50, regex="^[a-z]+$")
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

4. 声明必需参数与省略号

你可以使用 ... 来声明必需的查询参数:

@app.get("/items_required/")
def read_items_required(q: str = Query(..., min_length=3)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

使用 <font style="color:rgb(64, 64, 64);">...</font> 来声明必需的查询参数是一种明确的方式,表示该参数是必需的,不能省略

5. 声明更多元数据

你可以为查询参数声明更多的元数据,例如标题、描述等:

@app.get("/items_with_metadata/")
def read_items_with_metadata(q: str = Query(None,title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,)
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

声明详细的元数据可以帮助开发者更好的理解接口!

6.完整代码

代码如下,各位仔细测试就好!

from fastapi import FastAPI, Queryapp = FastAPI()# 1. 字符串参数限制
@app.get("/items/")
def read_items(q: str = Query(None, max_length=50)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results# 2. 增加额外的约束条件
@app.get("/items_with_constraints/")
def read_items_with_constraints(q: str = Query(None, min_length=3, max_length=50, regex="^[a-z]+$")
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results# 3. 最短字符长度与正则表达式
@app.get("/items_with_regex/")
def read_items_with_regex(q: str = Query(None, min_length=3, max_length=50, regex="^[a-z]+$")
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results# 4. 声明必需参数与省略号
@app.get("/items_required/")
def read_items_required(q: str = Query(..., min_length=3)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results# 5. 声明更多元数据
@app.get("/items_with_metadata/")
def read_items_with_metadata(q: str = Query(None,title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,)
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results# 运行 FastAPI 应用
if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)

总结

通过本文,我们深入探讨了 FastAPI 中的路径参数、查询参数及其检校机制。作者通过实际代码示例展示了如何使用 FastAPI 构建一个简单的 TodoList 应用,并详细介绍了路径参数和查询参数的各种用法和限制。希望本文能帮助你更好地理解和应用 FastAPI,提升各位入门者的 Python Web 开发技能。

OK!今天就学习到这里了!😎


相关链接

  • 项目地址:FastAPI-CookBook
  • 相关文档:专栏地址
  • 作者主页:GISer Liu-CSDN博客

thank_watch

如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star🌟也可以😂.


http://www.mrgr.cn/news/71946.html

相关文章:

  • Mysql数据库里的SSH连接
  • 320页PDF | 集团IT蓝图总体规划报告-德勤(限免下载)
  • 【时间之外】IT人求职和创业应知【31】
  • Elman 神经网络算法详解
  • 产品经理晋级-Axure中继器+动态面板制作美观表格
  • 「IDE」集成开发环境专栏目录大纲
  • 计算机毕业设计Hadoop+Spark高考推荐系统 高考分数线预测 知识图谱 高考数据分析可视化 高考大数据 大数据毕业设计 Hadoop 深度学习
  • 元宇宙及其技术
  • Flink CDC(SQL Client)连接 MySQL 数据库教程
  • 数据结构中的抽象数据类型、逻辑结构、存储结构等到底是什么?
  • Linux学习笔记之shell快速入门及相关变量
  • PYNQ 框架 - 中断(INTR)驱动
  • 阿里巴巴通义灵码推出Lingma SWE-GPT:开源模型的性能新标杆
  • 音视频入门基础:MPEG2-TS专题(4)——使用工具分析MPEG2-TS传输流
  • JavaScript案例-轮播图
  • LeetCode【0019】删除链表的倒数第N个结点
  • 论文3—《基于YOLOv5s的农田垃圾轻量化检测方法》文献阅读分析报告
  • 我是如何一步步学习深度学习模型PyThorch
  • 信息收集系列(二):ASN分析及域名收集
  • LLM - 使用 LLaMA-Factory 微调大模型 Qwen2-VL SFT(LoRA) 图像数据集 教程 (2)
  • Python 正则表达式使用指南
  • WSL与Ubuntu系统--使用Linux
  • 渗透测试---网络基础之HTTP协议与内外网划分
  • 实战指南:理解 ThreadLocal 原理并用于Java 多线程上下文管理
  • Ngxin隐藏服务名称和版本号(源码部署和Docker部署)
  • 【最少刷题数——二分】