前言
首先要说明的是,我对于 FastAPI ,自认为技术水平还不够,所以写出来的文章内容或许太过于浅显或者是简陋,不够详细完整
所以在最开始,我已经为各位整理了我认为 FastAPI 项目中可能会涉及到的一些技术的官方文档
祝各位开发一切顺利,永无 BUG !😉😋
一、FastAPI 简介
FastAPI 是一个用于构建 API 的现代、快速的 web 框架。它建立在 Starlette 和 Pydantic 之上,利用类型提示进行数据处理,并自动生成 API 文档
详细开发手册可以通过 官方文档 查看
客套话说完,来说我使用体验,FastAPI 的代码写起来着实 😋 清爽 😋,它用装饰器的方式清清白白的给你表明了 API 的路由地址
它和Flask很像,但是Flask、Django它们都属于【同步框架】,而FastAPI属于【异步框架】,如果你不理解什么是同步什么是异步,只需要知道,异步是一种释放性能、提高运行效率的手法,而且FastAPI还有很人性化的一点是它是同步异步混合型,如果你不习惯异步编程,那么你完全可以把它当作Flask用
事不宜迟,让我们快速开始使用FastAPI,让Django、SpringBoot它们感受一下快速成型的恐怖
二、FastAPI 正式教学
2.1 安装
pip install fastapi 安装 FastAPI
pip install uvicorn 安装 它的 ASGI 服务器
如果网络环境太差,可以用国内阿里源镜像
pip install fastapi uvicorn -i https://mirrors.aliyun.com/pypi/simple
2.2 第一个 FastAPI 应用
新建一个 main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def hello():
return "Hello,world!"
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", reload=True)当然你可以不写__main__,直接终端执行 uvicorn main:app --reload也行,我比较喜欢用这样的方式,这样可以让uvicorn的执行在代码里面就控制好了
uvicorn 服务器的默认地址是 http://127.0.0.1:8000,也就是本地的8000端口,如果你想改变可以在 uvicorn.run() 里面设置 host 和 port
代码解释
-
from fastapi import FastAPI导入 FastAPI 类,它是 FastAPI 的核心 -
app = FastAPI()定义出 app 为 FastAPI 的具体实例,后面的所有路由地址都将由它衍生出去,可以把它理解为根地址 @app.get("/")这是 FastAPI 的装饰器
@app.get()
@app.post()
@app.put()
@app.patch()
@app.delete()
所有常见的请求方式它都有,里面跟上路由地址即可-
def hello()这个函数表示是当请求发送给 http://127.0.0.1:8000/ 时触发这个hello函数 -
return "Hello,world!"这个是返回值,如果请求成功,那么这个值会被返回给请求方的浏览器或者是 API 客户端
2.3 交互文档
FastAPI有一大顶级功能——内置的 API 交互文档,让你不再需要Postman😋
默认地址是 http://127.0.0.1:8000/docs 和 http://127.0.0.1:8000/redoc
如果你想改变它的路由地址,具体设置在 app 实例里面
app = FastAPI(
docs_url="/docs",
redoc_url="/redoc",
)具体使用你只需要点进去立马会用,就是一个很普通的发送请求查看具体结果,如果发生错误了会在启动的终端里面报出来
主要需要讲一下的是,它会经常性的加载不出来或者加载很不稳定,本质是当前网络连接外网 CDN 比较慢
这里提供一个我常用的解决方法,就是用fastapi_offline,它本质是把所有资源包给你打包好直接下载到本地用,保证一定能加载运行,pip install fastapi-offline即可
from fastapi import FastAPI
app = FastAPI()
改成
from fastapi_offline import FastAPIOffline
app = FastAPIOffline()2.4 路由操作
app 路由
这个就是简单的直接由 app 发出的路由地址
from fastapi import FastAPI
app = FastAPI()
# http://127.0.0.1:8000/hello
@app.get("/hello")
def hello():
return "Hello!"
# http://127.0.0.1:8000/hello/world
@app.get("/hello/world")
def helloworld():
return "Hello,world!"router 路由
这个相当于给 app 总的路由分配子路由,具体使用也很简单
from fastapi import APIRouter
router = APIRouter(prefix="/router")
# prefix是必须设置的,如果不设置则这个router路由无效
@router.get("/hello") # 它的具体请求地址为127.0.0.1:8000/router/hello
def router_hello():
return "这是Router下面的Hello"
@router.get("/world") # 它的具体请求地址为127.0.0.1:8000/router/world
def router_world():
return "这是Router下面的World"
# --------------------------------------------
from fastapi import FastAPI
app = FastAPI()
app.include_router(router)
# prefix还可以在app.include_router里面设置,比如有时的批量加入router
@app.get("/hello")
def hello():
return "这是App下面的Hello"通常来说 router 路由是分文件编写,一般不和 app 写在一个文件内
例如文件结构为:
project/
| - main.py # app = FastAPI()
| - user.py # user = APIRouter(prefix="/user")
| - home.py # home = APIRouter(prefix="/home")
在 main.py 内 app.include_router(user), app.include_router(home)2.5 请求响应
请求参数
我们通常会见到一些 URL 地址类似于 https://xxx.xxx.com/?x=xxx&x=xxx
?后面的就是查询参数,FastAPI 正是利用查询参数作为函数的传入参
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def get_args(arg1: int, arg2: str):
return {
"arg1": arg1,
"arg2": arg2,
}当我们访问 http://127.0.0.1:8000/?arg1=10&arg2=helloworld 时则会返回 {“arg1”:10,“arg2”:“helloworld”}
我们还可以把请求参直接写在路由里面,比如
@app.get("/{arg1}")
def get_args(arg1: int, arg2: str):
return {
"arg1": arg1,
"arg2": arg2,
}此时我们需要访问的路由地址变成了 http://127.0.0.1:8000/10?arg2=helloworld ,arg1 的 10 不再作为查询参数放在?后面,而是直接加入在 URL 地址内,而 arg2 仍然是查询参数
数据响应
- 基本数据返回: 对应网址请求里面的
application/text,也就是各种类型的数据
@app.get("/xxx")
def func():
...- Json 数据返回: 对应网址请求里面的
application/json
from fastapi.responses import JSONResponse
@app.get("/xxx")
def func() -> JSONResponse:
...
return JSONResponse(context="...")- Pydantic 模型返回: 自定义的数据模型返回,基于
pydantic的BaseModel
from fastapi import FastAPI
from pydantic import BaseModel
class DemoModel(BaseModel):
username: str
password: str
app = FastAPI()
@app.post("/")
def Demo(demo: DemoModel):
return demo2.6 依赖项
依赖项是指:一个可以多次复用的函数或对象,比如常见的数据库连接,身份验证等等
以 Mysql 数据库为例,我们希望的是
- 项目启动时连接上数据库
- 项目终止时关闭数据库
- 运行过程中可以访问数据库进行增删改查
from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI
from pymysql import connect
# 数据库配置
DB_CONFIG = {
"host": "localhost",
"port": 3306,
"user": "your_username",
"password": "your_password",
"db": "your_database",
"charset": "utf8mb4",
}
db = None
# 连接数据库
def connect_db():
global db
db = connect(**DB_CONFIG)
print("数据库连接成功")
# 断开数据库连接
def disconnect_db():
global db
if db:
db.close()
print("数据库断开连接")
db = None
# 获取数据库
def get_db():
return db
# 这个是Fastapi的生命周期管理,是一个异步函数
# 由于以pymysql为例,数据库的连接和关闭需要使用同步函数
@asynccontextmanager
async def lifespan(app: FastAPI):
connect_db()
yield
disconnect_db()
app = FastAPI(lifespan=lifespan)
@app.get("/all")
def select_all(db = Depends(get_db)): # 把 get_db 函数作为依赖项注入给了db变量,从而使得函数内部可以以它作为数据库进行查询
with db.cursor() as cursor:
cursor.execute("select * from your_table_name")
result = cursor.fetchall()
return result
@app.get("/single")
def select_single(db = Depends(get_db)): # 再次复用 get_db 函数作为依赖项
with db.cursor() as cursor:
cursor.execute("select * from your_table_name where id=xxx")
result = cursor.fetchall()
return result2.7 阶段性总结
到此,你已经掌握了 70% 的 FastAPI,你可能会对此感到惊讶,明明当初学 Springboot,学 Django 的时候好像不是这样的啊,但是事实就是如此,不要忘了 FastAPI 的核心——轻量级 Web 框架,如果使用不简单,那怎么谈轻量
三、项目开发推荐
3.1 项目文件结构
这是我比较喜欢的一种项目结构方式
类似于 Django 的风格
project/
| - page1/
| | - __init__.py
| | - models.py # 数据库ORM模型 -> sqlacalchemy
| | - schemas.py # 数据验证模型 -> pydantic
| | - service.py # 业务代码
| | - router.py # 路由配置
|
| - page2/
| | - __init__.py
| | - models.py
| | - schemas.py
| | - service.py
| | - router.py
|
| - core/
| | - __init__.py
| | - setting.py # 全局配置
| | - database.py # 数据库连接
| | - logger.py # 日志管理
|
| - __init__.py
| - main.py # 主入口3.2 ORM 数据库推荐 (任选其一即可)
-
SqlAcalchemy(经典 ORM,可以搭配Alembic实现数据库迁移) -
SqlModel(基于Pydantic和SQLAlchemy实现) -
Tortoise-ORM(类似Django-ORM的实现)
3.3 获取 uvicorn 自带的日志
import logging
# uvicorn 主日志器
# 根据情况使用不同级别日志
uvicorn_logger = logging.getLogger("uvicorn")
# uvicorn 访问日志器
# INFO 级别日志
uvicorn_access_logger = logging.getLogger("uvicorn.access")
# uvicorn 错误日志器
# ERROR 级别日志
uvicorn_error_logger = logging.getLogger("uvicorn.error")3.4 异步编程
以上所涉及到的代码,都是使用同步的方式来编写的,主要是为了初学者方便理解
但是想要彻底释放 FastAPI 相对于 Django、Flask 的性能优势,那么就需要使用异步方式来编写每个函数
如果数据表本身数据量小,用户的每秒请求数少,硬件性能高,网络延迟低,那么其实同步和异步的方式在实际运行过程中的区别并不大,具体编程方式因个人喜好而定,反正至少 FastAPI 是个同步异步混合型框架,你可以自由的开发它
借用上面的数据库依赖项的代码示例,但是这次是纯异步方式
from contextlib import asynccontextmanager
from aiomysql import connect
from fastapi import Depends, FastAPI
DB_CONFIG = {
"host": "localhost",
"port": 3306,
"user": "your_username",
"password": "your_password",
"db": "your_database",
"charset": "utf8mb4",
}
db = None
async def connect_db():
global db
db = await connect(**DB_CONFIG)
print("数据库连接成功")
async def disconnect_db():
global db
if db:
db.close()
print("数据库断开连接")
db = None
async def get_db():
return db
@asynccontextmanager
async def lifespan(app: FastAPI):
await connect_db()
yield
await disconnect_db()
app = FastAPI(lifespan=lifespan)
@app.get("/all")
async def select_all(db=Depends(get_db)):
async with db.cursor() as cursor:
await cursor.execute("select * from your_table_name")
result = await cursor.fetchall()
return result
















