FastApi快速入门(一)
写在前面
本文为学习官网教程的总结和概括,并结合实际应用给出一些例子,建议结合官网教程阅读。
本小节主要是请求参数的使用方法,下一小节是响应模型和其他。
快速开始
1. 创建virtualenv环境
virtualenv venv
source venv/bin/activate
2. 安装所需依赖
- 安装所有的可选依赖及对应功能(开发环境推荐)
pip install "fastapi[all]"
- 选装依赖(生产环境推荐)
安装fastapi并且安装
uvicorn
来作为服务器:
pip install fastapi
pip install "uvicorn[standard]"
3. 基本代码
新建main.py
,复制以下内容
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "你好,世界"}
4.运行服务
- 方式一:终端直接运行
uvicorn main:app 命令含义如下:
main:main.py 文件(一个 Python「模块」)。
app:在 main.py 文件中通过 app = FastAPI() 创建的对象。
--reload:让服务器在更新代码后重新启动。仅在开发时使用该选项。
uvicorn main:app --reload
- 方式二:在main文件中添加运行代码
修改main.py
文件如下,设置好基本参数host
和port
。
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
async def root():
return {"message": "你好,世界"}
if __name__ == '__main__':
uvicorn.run(app=app,host="127.0.0.1",port=8000)
# 使用reload可以在修改代码时自动刷新,注意必须要 `main:app` 这样配置,其中main是文件名,app是实例化FastAPI的应用名称。
# uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
在终端运行
python main.py
文档
fastapi自动生成文档,可以通过http://127.0.0.1:8000/docs访问可交互式文档,也可以通过http://127.0.0.1:8000/redoc访问另外一个类型的文档。
基本HTTP请求参数
基本HTTP请求参数包括:路径参数
、查询参数
、请求体
。
requests.post('http://example.com/article/123456/?page=10&limit=20',data={'a':1,'b':2})
在上述例子中,123456
为路径参数,page
和limit
为查询参数,data
为请求体。
路径参数
以下例子为未指定类型
的路径参数请求方法。
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
可以通过请求http://127.0.0.1:8000/items/foo获得如下结果。
{"item_id":"foo"}
有类型的路径参数
可以使用标准的 Python 类型标注为函数中的路径参数声明类型。
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
数据转换和数据校验
当指定参数类型,fastapi会自动完成数据类型的转换,如果不能转换,也会自动校验并指出了校验未通过的具体原因。
预设值(枚举,为参数提供选项)
导入 Enum 并创建一个继承自 str
和 Enum
的子类。
通过从 str
继承,API 文档将能够知道这些值必须为 string
类型并且能够正确地展示出来。
然后创建具有固定值的类属性,这些固定值将是可用的有效值:
from enum import Enum
from fastapi import FastAPI
import uvicorn
class ModelName(str, Enum):
student1 = "张三"
student2 = "李四"
student3 = "王五"
app = FastAPI()
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.student1:
return {"model_name": model_name, "message": "这是张三!"}
if model_name.value == "李四":
return {"model_name": model_name, "message": "这是李四"}
return {"model_name": model_name, "message": "是王五呀"}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
包含路径的路径参数
你可以使用直接来自 Starlette 的选项来声明一个包含路径的路径参数:
/files/{file_path:path}
比如可以通过以下代码:
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
查询参数
声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数
from fastapi import FastAPI
import uvicorn
app = FastAPI()
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
查询字符串是键值对的集合,这些键值对位于 URL 的 ? 之后,并以 & 符号分隔。
例如,在以下 url 中:
http://127.0.0.1:8000/items/?skip=0&limit=10
查询参数为:
- skip:对应的值为 0
- limit:对应的值为 10
可选参数
通过同样的方式,你可以将它们的默认值设置为 None
来声明可选查询参数:
from typing import Union
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: Union[str, None] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
在这个例子中,函数参数 q
将是可选的,并且默认值为 None
。
查询参数类型转换
你还可以声明 bool
类型,1
True
true
on
yes
或任何其他的变体形式(大写,首字母大写等等),将被自动转换为 True
。对于值为 False
的情况也是一样的。
必须查询参数
- 当你为非路径参数声明了默认值时(目前而言,我们所知道的仅有查询参数),则该参数不是必需的。
- 如果你不想添加一个特定的值,而只是想使该参数成为可选的,则将默认值设置为
None
。 - 但当你想让一个查询参数成为必需的,不声明任何默认值就可以。
请求体
我们使用 Pydantic
模型来声明请求体,并能够获得它们所具有的所有能力和优点。
from typing import Union,List
from fastapi import FastAPI
import uvicorn
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: List[float]
tax: Union[float, None] = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
如果你不想使用 Pydantic
模型,你还可以使用 Body 参数。
from typing import Union
from fastapi import Body, FastAPI
import uvicorn
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
class User(BaseModel):
username: str
full_name: Union[str, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: int = Body()):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
这个例子中, importance
字段用 Body
指示 FastAPI 将其作为请求体的另一个键进行处理。
总结
- 如果在路径中也声明了该参数,它将被用作路径参数。
- 如果参数属于单一类型(比如 int、float、str、bool 等)它将被解释为查询参数。
- 如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体。
查询参数和字符串校验
from typing import Union
from fastapi import FastAPI, Query
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None, min_length=3, max_length=50, regex="^fixedquery$"
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
- 使用
Query
指示参数为查询参数,并通过这个方法进行约束,比如regex
参数为正则表达式。
声明为必需参数
- 不指定
default
参数,则声明为必须参数。 - 使用省略号(...)声明必需参数
from fastapi import FastAPI, Query
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default=..., min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
如果你之前没见过
...
这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为「省略号」。 Pydantic 和 FastAPI 使用它来显式的声明需要一个值。
- 使用Pydantic中的Required代替省略号(
...
)¶
from fastapi import FastAPI, Query
import uvicorn
from pydantic import Required
app = FastAPI()
@app.get("/items/")
async def read_items(q: str = Query(default=Required, min_length=3)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
查询参数列表/多个值
from typing import List, Union
from fastapi import FastAPI, Query
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):
query_items = {"q": q}
return query_items
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
可以用以下网址请求,那么q获取到的是一个列表。
http://localhost:8000/items/?q=foo&q=bar
声明更多元数据
from typing import List, Union
from fastapi import FastAPI, Query
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(
q: Union[str, None] = Query(
default=None,
title="q参数的标题", #设置标题
description="这是q查询参数", #查询参数的描述
alias="item-query", # 可以用来重命名传参名称,一般可以用来避免参数在python中不规范,或者前端特别要求。
min_length=3,
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
路径参数和数值校验
与使用 Query
为查询参数声明更多的校验和元数据的方式相同,你也可以使用 Path
为路径参数声明相同类型的校验和元数据。
from typing import Union
from fastapi import FastAPI, Path, Query
import uvicorn
app = FastAPI()
@app.get("/items/{item_id}")
async def read_items(
item_id: int = Path(title="The ID of the item to get"),
q: Union[str, None] = Query(default=None, alias="item-query"),
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
数值校验
ge
: 大于或等于gt
: 大于le
: 小于或等于lt
: 小于
Cookie 参数
你可以像定义 Query
参数和 Path
参数一样来定义 Cookie
参数。
from typing import Union
from fastapi import Cookie, FastAPI
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: Union[str, None] = Cookie(default=None)):
return {"ads_id": ads_id}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
因为自带的swagger
测试不了,下面提供requests
中的cookiejar
方法的代码。
import requests
# 创建会话
s = requests.Session()
# 创建一个Cookie Jar对象
jar = requests.cookies.RequestsCookieJar()
# 向Cookie Jar对象中添加cookie值
jar.set("ads_id", "my_test_cookie")
# 把cookies追加到Session中
s.cookies.update(jar)
r = s.get('https://test.percy.fun/items/')
print(r.json())
Header 参数
你可以使用定义 Query
, Path
和 Cookie
参数一样的方法定义 Header
参数。
from typing import Union
from fastapi import FastAPI, Header
import uvicorn
app = FastAPI()
@app.get("/items/")
async def read_items(user_agent: Union[str, None] = Header(default=None)):
return {"User-Agent": user_agent}
if __name__ == '__main__':
uvicorn.run(app="main:app",host="0.0.0.0",port=8000,reload=True)
自动转换
大多数标准的headers用 "连字符" 分隔,也称为 "减号" (-)。
但是像 user-agent 这样的变量在Python中是无效的。
因此, 默认情况下, Header 将把参数名称的字符从下划线 (_) 转换为连字符 (-) 来提取并记录 headers.