在开发restful接口的时候,会遇到接口路径带参数的情况,比如
- 查询单个 book 接口:
get
/api/v1/book/{id}
- 修改单个 book 接口:
put
/api/v1/book/{id}
- 删除单个 book 接口: delete
/api/v1/book/{id}
这里路径里面的 {id}
就是路径参数
路径参数
你可以使用与 Python 格式化字符串相同的语法来声明路径”参数”或”变量”:
代码语言:javascript复制from fastapi import FastAPI
app = FastAPI()@app.get("/items/{item_id}")async def read_item(item_id):
return {"item_id": item_id}
路径参数 item_id
的值将作为参数 item_id
传递给你的函数。
所以,如果你运行示例并访问 http://127.0.0.1:8000/items/foo
,将会看到如下响应:
{"item_id":"foo"}
有类型的路径参数
你可以使用标准的 Python 类型标注为函数中的路径参数声明类型。
代码语言:javascript复制from fastapi import FastAPI
app = FastAPI()@app.get("/items/{item_id}")async def read_item(item_id: int):
return {"item_id": item_id}
在这个例子中,item_id
被声明为 int
类型。
小笔记:这将为你的函数提供编辑器支持,包括错误检查、代码补全等等。
数据转换
如果你运行示例并打开浏览器访问 http://127.0.0.1:8000/items/3
,将得到如下响应:
{"item_id":3}
小笔记:注意函数接收(并返回)的值为 3,是一个 Python
int
值,而不是字符串"3"
。 所以,FastAPI 通过上面的类型声明提供了对请求的自动”解析”。
数据校验
但如果你通过浏览器访问 http://127.0.0.1:8000/items/foo
,你会看到一个清晰可读的 HTTP 错误:
{ "detail": [
{ "loc": [ "path", "item_id"
], "msg": "value is not a valid integer", "type": "type_error.integer"
}
]
}
因为路径参数 item_id
传入的值为 "foo"
,它不是一个 int
。
如果你提供的是 float
而非整数也会出现同样的错误,比如:http://127.0.0.1:8000/items/4.2
小笔记: 所以,通过同样的 Python 类型声明,FastAPI 提供了数据校验功能。 注意上面的错误同样清楚地指出了校验未通过的具体原因。 在开发和调试与你的 API 进行交互的代码时,这非常有用。
Pydantic
所有的数据校验都由 Pydantic 在幕后完成,所以你可以从它所有的优点中受益。并且你知道它在这方面非常胜任。
你可以使用同样的类型声明来声明 str
、float
、bool
以及许多其他的复合数据类型。
本教程的下一章节将探讨其中的一些内容。
顺序很重要
在创建路径操作时,你会发现有些情况下路径是固定的。
比如 /users/me
,我们假设它用来获取关于当前用户的数据.
然后,你还可以使用路径 /users/{user_id}
来通过用户 ID 获取关于特定用户的数据。
由于 路径操作 是按顺序依次运行的,你需要确保路径 /users/me
声明在路径 /users/{user_id}
之前:
from fastapi import FastAPI
app = FastAPI()@app.get("/users/me")async def read_user_me():
return {"user_id": "the current user"}@app.get("/users/{user_id}")async def read_user(user_id: str):
return {"user_id": user_id}
否则,/users/{user_id}
的路径还将与 /users/me
相匹配,接收一个值为 "me"
的 user_id
参数。
预设值
如果你有一个接收路径参数的路径操作,但你希望预先设定可能的有效参数值,则可以使用标准的 Python Enum
枚举类型
创建一个 Enum
枚举类,导入 Enum
并创建一个继承自 str
和 Enum
的子类。
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet" # 实例 model_name = ModelName('lenet')
print(model_name) # ModelName.lenet print(model_name.value) # lenet
通过从 str
继承,API 文档将能够知道这些值必须为 string
类型并且能够正确地展示出来。
然后创建具有固定值的类属性,这些固定值将是可用的有效值:
from enum import Enumfrom fastapi import FastAPIclass ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"app = FastAPI()@app.get("/models/{model_name}")async def get_model(model_name: ModelName):
if model_name is 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"}
分步概括
步骤 1.声明路径参数
然后使用你定义的枚举类(ModelName
)创建一个带有类型标注的路径参数:
async def get_model(model_name: ModelName):
因为已经指定了路径参数的可用值,所以交互式文档可以恰当地展示它们:
![[Pasted image 20230712163334.png]]
步骤 2.获取枚举值
你可以使用 model_name.value
或通常来说 your_enum_member.value
来获取实际的值(在这个例子中为 str
)
if model_name is 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"}
你也可以通过 ModelName.lenet.value
来获取值 "lenet"
。
包含路径的路径参数
假设你有一个路径操作,它的路径为 /files/{file_path}
。
但是你需要 file_path
自身也包含路径,比如 home/johndoe/myfile.txt
。
因此,该文件的URL将类似于这样:/files/home/johndoe/myfile.txt
。
路径转换器
你可以使用直接来自 Starlette 的选项来声明一个包含路径的路径参数:
代码语言:javascript复制/files/{file_path:path}
在这种情况下,参数的名称为 file_path
,结尾部分的 :path
说明该参数应匹配任意的路径。
因此,你可以这样使用它:
代码语言:javascript复制from fastapi import FastAPI
app = FastAPI()@app.get("/files/{file_path:path}")async def read_file(file_path: str):
return {"file_path": file_path}
小笔记: 你可能会需要参数包含
/home/johndoe/myfile.txt
,以斜杠(/
)开头。 在这种情况下,URL 将会是/files//home/johndoe/myfile.txt
,在files
和home
之间有一个双斜杠(//
)。
总结
使用 FastAPI,通过简短、直观和标准的 Python 类型声明,你将获得:
- 编辑器支持: 错误检查,代码补全等
- 数据 “解析”
- 数据校验
- API 标注和自动生成的文档
而且你只需要声明一次即可。 这可能是 FastAPI 与其他框架相比主要的明显优势(除了原始性能以外)。
几种类型路径参数: 1.声明int类型
代码语言:javascript复制@app.get("/items/{item_id}")async def read_item(item_id: int):
return {"item_id": item_id}
2.声明str类型
代码语言:javascript复制@app.get("/users/{user_id}")async def read_user(user_id: str):
return {"user_id": user_id}
3.Enum
枚举类型
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"@app.get("/models/{model_name}")async def get_model(model_name: ModelName):
4.path路径类型
代码语言:javascript复制@app.get("/files/{file_path:path}")async def read_file(file_path: str):
return {"file_path": file_path}