来自:掘金,作者:宇宙之一粟
链接:https://juejin.cn/post/7219225476706140218
本文翻译自 Moving from Flask to FastAPI, 作者:Amal Shaji
刚好笔者这几天上手体验 FastAPI,感受到这个框架易用和方便。之前也使用过 Python 中的 Django 和 Flask 作为项目的框架。Django 说实话上手也方便,但是学习起来有点重量级框架的感觉,FastAPI 带给我的直观体验还是很轻便的,本文翻译的这篇文章就会着重介绍 FastAPI 和 Flask 的区别。
Python 是最流行的编程语言之一。从脚本到 API 开发再到机器学习,Python 都有着它自己的足迹。因为 Python 注重开发者的体验和其所能提供的大量工具而大受欢迎。网络框架 Flask 就是这样一个工具,它在机器学习社区中很受欢迎。它也被广泛用于 API开发。但是有一个新的框架正在崛起:FastAPI。与 Flask 不同,FastAPI 是一个 ASGI(Asynchronous Server Gateway Interface 异步服务器网关接口)框架。与 Go 和 NodeJS 一样,FastAPI 是最快的基于 Python 的 Web 框架之一。
本文针对那些有兴趣从 Flask 转移到 FastAPI 的人,比较和对比了 Flask 和 FastAPI 的常见模式。
#
FastAPI vs Flask
FastAPI 的构建考虑了以下三个主要问题:
你可以把 FastAPI 看作是把 Starlette、Pydantic、OpenAPI 和 JSON Schema 粘合在一起的胶水。
-
本质上说,FastAPI 使用 Pydantic 进行数据验证,并使用 Starlette 作为工具,使其与 Flask 相比快得惊人,具有与 Node 或 Go 中的高速 Web APIs 相同的性能。
-
Starlette + Uvicorn 提供异步请求能力,这是 Flask 所缺乏的。
-
有了 Pydantic 以及类型提示,你就可以得到一个具有自动完成功能的良好的编辑体验。你还可以得到数据验证、序列化和反序列化(用于构建一个 API),以及自动化文档(通过 JSON Schema 和 OpenAPI )。
也就是说,Flask 的使用更为广泛,所以它经过了实战检验,并且有更大的社区支持它。由于这两个框架都是用来扩展的,Flask 显然是赢家,因为它有庞大的插件生态系统。
建议:
#
开始
安装
与任何其他 Python 包一样,安装非常简单。
Flask
pip install flask
# or
poetry add flask
pipenv install flask
conda install flask
FastAPI
pip install fastapi uvicorn
# or
poetry add fastapi uvicorn
pipenv install fastapi uvicorn
conda install fastapi uvicorn -c conda-forge
与 Flask 不同,FastAPI 没有内置的开发服务器,因此需要像 Uvicorn 或 Daphne 这样的 ASGI 服务器。
"Hello World" 应用
Flask
# flask_code.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return {"Hello": "World"}
if __name__ == "__main__":
app.run()
FastAPI
# fastapi_code.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def home():
return {"Hello": "World"}
if __name__ == "__main__":
uvicorn.run("fastapi_code:app")
像
reload=True
这样的参数可以被传递到
uvicorn.run()
中,以实现开发时的热重载。
或者,您可以直接从终端启动服务器:
uvicorn run fastapi_code:app
热加载模式:
uvicorn run fastapi_code:app --reload
#
配置
Flask 和 FastAPI 都提供了许多选项来处理不同环境的不同配置。两者都支持以下模式:
-
环境变量
-
配置文件
-
实例文件夹
-
类和继承
有关更多信息,请参阅其各自的文档:
Flask
import os
from flask import Flask
class Config(object):
MESSAGE = os.environ.get("MESSAGE")
app = Flask(__name__)
app.config.from_object(Config)
@app.route("/settings")
def get_settings():
return { "message": app.config["MESSAGE"] }
if __name__ == "__main__":
app.run()
现在,在你运行服务器之前,设置适当的环境变量:
export MESSAGE="hello, world"
FastAPI
import uvicorn
from fastapi import FastAPI
from pydantic import BaseSettings
class Settings(BaseSettings):
message: str
settings = Settings()
app = FastAPI()
@app.get("/settings")
def get_settings():
return { "message": settings.message }
if __name__ == "__main__":
uvicorn.run("fastapi_code:app")
同样,在运行服务器之前,设置适当的环境变量:
export MESSAGE="hello, world"
#
路由, 模板和视图
HTTP 方法
Flask
from flask import request
@app.route("/", methods=["GET", "POST"])
def home():
# handle POST
if request.method == "POST":
return
{"Hello": "POST"}
# handle GET
return {"Hello": "GET"}
FastAPI
@app.get("/")
def home():
return {"Hello": "GET"}
@app.post("/")
def home_post():
return {"Hello": "POST"}
FastAPI 为每个方法提供单独的装饰器:
@app.get("/")
@app.post("/")
@app.delete("/")
@app.patch("/")
URL 参数
通过 URL(如
/employee/1
)传递信息以管理状态:
Flask
@app.route("/employee/")
def home():
return {"id": id}
FastAPI
@app.get("/employee/{id}")
def home(id: int):
return {"id": id}
URL参数的指定类似于一个 f-string 表达式。此外,你还可以利用类型提示。这里,我们在运行时告诉 Pydantic,
id
是
int
类型的。在开发中,这也可以帮助完成更好的代码完成度。
查询参数
与 URL 参数一样,查询参数(如
/employee?department=sales
)也可用于管理状态(通常用于过滤或排序):
Flask
from flask import request
@app.route("/employee")
def home():
department = request.args.get("department")
return {"department": department}
FastAPI
@app.get("/employee")
def home(department: str):
return {"department": department}
模板
Flask
from flask import
render_template
@app.route("/")
def home():
return render_template("index.html")
默认情况下,Flask会在 "templates "文件夹中寻找模板。
FastAPI
你需要安装 Jinja:
pip install jinja2
实现:
from fastapi import Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/", response_class=HTMLResponse)
def home(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
对于 FastAPI,你需要明确地定义 "模板 "文件夹。然后对于每个响应,需要提供请求上下文。
静态文件
Flask
默认情况下,Flask 从“static”文件夹中提供静态文件。
FastAPI
在 FastAPI 中,需要为静态文件挂载一个文件夹:
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
异步任务
Flask
从 Flask 2.0 开始,您可以使用
async/await
创建异步路由处理程序:
@app.route("/")
async def home():
result = await some_async_task()
return result
有关 Flask 中异步视图的更多信息,请查看 Flask 2.0 中的异步一文。
Flask 中的异步也可以通过使用线程(并发)或多处理(并行)或 Celery 或 RQ 等工具来实现:
-
Asynchronous Tasks with Flask and Celery:https://testdriven.io/blog/flask-and-celery/
-
Asynchronous Tasks with Flask and Redis Queue:https://testdriven.io/blog/asynchronous-tasks-with-flask-and-redis-queue/
FastAPI
由于 FastAPI 对 asyncio 的原生支持,它极大地简化了异步任务。要使用的话,只需在视图函数中添加
async
关键字:
@app.get("/")
async def home():
result = await some_async_task()
return result
FastAPI 还具有后台任务功能,您可以使用它来定义返回响应后要运行的后台任务。这对于不需要在发送回响应之前完成的操作很有用。
from fastapi import BackgroundTasks
def process_file(filename: str):
# process file :: takes minimum 3 secs (just an example)
pass
@app.post("/upload/{filename}")
async def upload_and_process(filename: str, background_tasks: BackgroundTasks):
background_tasks.add_task(process_file, filename)
return {"message": "processing file"}
在这里,响应将被即时发送,而不会让用户等待文件处理完成。
当你需要进行繁重的后台计算时,或者你需要一个任务队列来管理任务(tasks)和工作者(workers)时,你可能想使用Celery 而不是
BackgroundTasks
。更多内容请参考 FastAPI 和 Celery 的异步任务:https://testdriven.io/blog/fastapi-and-celery/
依赖注入
Flask
虽然你可以实现自己的依赖注入解决方案,但 Flask 默认没有真正的一流支持。相反,你需要使用一个外部包,如 flask-injector。
FastAPI
另一方面,FastAPI 具有处理依赖注入的强大解决方案。
例如:
from databases import Database
from fastapi import Depends
from starlette.requests import Request
from db_helpers import get_all_data
def get_db(request: Request):
return request.app.state._db
@app.get("/data")
def get_data(db: Database = Depends(get_db)):
return get_all_data(db)
因此,
get_db
将获取对在应用程序的启动事件处理程序中创建的数据库连接的引用。Depends 然后用于向 FastAPI 指示路由“依赖于”
get_db
。因此,它应该在路由处理程序中的代码之前执行,并且结果应该“注入”到路由本身。
数据校验
Flask
Flask 没有任何内部数据验证支持。您可以使用功能强大的 Pydantic 包通过 Flask-Pydantic 进行数据验证。
FastAPI
FastAPI 如此强大的原因之一是它支持 Pydantic。
from pydantic import BaseModel
app = FastAPI()
class Request(BaseModel):
username: str
password: str
@app.post("/login")