FastAPI for Flask Users
https://amitness.com/2020/06/fastapi-vs-flask/
虽然 Flask 已成为机器学习项目中 API 开发的实际选择,但有一个名为 FastAPI 的新框架已经获得了很多社区的关注.
我最近决定通过移植一个生产 Flask 项目来尝试一下 FastAPI。 从 Flask 获取 FastAPI 非常容易,我能够在几个小时内启动并运行.
自动数据验证、文档生成和 pydantic 模式和 python 类型等内置最佳实践的额外好处使其成为未来项目的有力选择.
在这篇文章中,我将通过对比 Flask 和 FastAPI 中各种常见用例的实现来介绍 FastAPI.
版本信息:
在撰写本文时,Flask 版本为 1.1.2,FastAPI 版本为 0.58.1
安装永久链接
PyPI 上提供了 Flask 和 FastAPI。 对于 conda,您需要使用 conda-forge
安装 FastAPI 的通道,而它在 Flask 的默认通道中可用.
烧瓶:
pip install flask
conda install flask
快速API:
pip install fastapi uvicorn
conda install fastapi uvicorn -c conda-forge
运行“Hello World”永久链接
烧瓶:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return {'hello': 'world'}
if __name__ == '__main__':
app.run()
现在您可以使用以下命令运行开发服务器。 它默认在 5000 端口上运行.
python app.py
快速API
# app.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def home():
return {'hello': 'world'}
if __name__ == '__main__':
uvicorn.run(app)
FastAPI 将服务推迟到一个生产就绪的服务器,称为 uvicorn
. 我们可以在开发模式下使用默认端口运行它 8000.
python app.py
生产服务器永久链接
烧瓶:
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return {'hello': 'world'}
if __name__ == '__main__':
app.run()
对于生产服务器, gunicorn
是Flask中常见的选择.
gunicorn app:app
快速API
# app.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def home():
return {'hello': 'world'}
if __name__ == '__main__':
uvicorn.run(app)
FastAPI 将服务推迟到一个生产就绪的服务器,称为 优维康. 我们可以启动服务器:
uvicorn app:app
您也可以通过运行以热重载模式启动它
uvicorn app:app --reload
此外,您还可以更改端口.
uvicorn app:app --port 5000
工人数量也可以控制.
uvicorn app:app --workers 2
您可以使用 gunicorn
也可以使用以下命令管理 uvicorn。 所有常规 gunicorn 标志,例如工人数量(-w
) 工作.
gunicorn -k uvicorn.workers.UvicornWorker app:app
HTTP 方法永久链接
烧瓶:
@app.route('/', methods=['POST'])
def example():
...
快速API:
@app.post('/')
def example():
...
每个 HTTP 方法都有单独的装饰器方法.
@app.get('/')
@app.put('/')
@app.patch('/')
@app.delete('/')
网址变量永久链接
我们想从 URL 中获取用户 ID,例如. /users/1
然后将用户id返回给用户.
烧瓶:
@app.route('/users/<int:user_id>')
def get_user_details(user_id):
return {'user_id': user_id}
快速API:
在 FastAPI 中,我们利用 Python 中的类型提示来指定所有数据类型。 例如,这里我们指定 user_id
应该是一个整数。 URL 路径中的变量也被指定为类似于 f-strings.
@app.get('/users/{user_id}')
def get_user_details(user_id: int):
return {'user_id': user_id}
查询字符串永久链接
我们希望允许用户通过使用查询字符串来指定搜索词 ?q=abc
在网址中.
烧瓶:
from flask import request
@app.route('/search')
def search():
query = request.args.get('q')
return {'query': query}
快速API:
@app.get('/search')
def search(q: str):
return {'query': q}
JSON POST 请求永久链接
让我们举一个玩具示例,我们想发送一个 JSON POST 请求,其中包含 text
键并取回小写版本.
# Request
{"text": "HELLO"}
# Response
{"text": "hello"}
烧瓶:
from flask import request
@app.route('/lowercase', methods=['POST'])
def lower_case():
text = request.json.get('text')
return {'text': text.lower()}
快速API:
如果您只是从 Flask 复制功能,您可以在 FastAPI 中按照以下方式进行操作.
from typing import Dict
@app.post('/lowercase')
def lower_case(json_data: Dict):
text = json_data.get('text')
return {'text': text.lower()}
但是,这正是 FastAPI 引入了一个新概念的地方,即创建映射到正在接收的 JSON 数据的 Pydantic 模式。 我们可以使用 pydantic 重构上面的例子:
from pydantic import BaseModel
class Sentence(BaseModel):
text: str
@app.post('/lowercase')
def lower_case(sentence: Sentence):
return {'text': sentence.text.lower()}
正如所见,JSON 数据不是获取字典,而是转换为模式的对象 Sentence
. 因此,我们可以使用数据属性访问数据,例如 sentence.text
. 这也提供了数据类型的自动验证。 如果用户尝试发送字符串以外的任何数据,他们将收到自动生成的验证错误.
示例无效请求
{"text": null}
自动响应
{
"detail": [
{
"loc": [
"body",
"text"
],
"msg": "none is not an allowed value",
"type": "type_error.none.not_allowed"
}
]
}
上传文件永久链接
让我们创建一个 API 来返回上传的文件名。 上传文件时使用的密钥将是 file
.
烧瓶
Flask 允许通过请求对象访问上传的文件.
# app.py
from flask import Flask, request
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files.get('file')
return {'name': file.filename}
快速API:
FastAPI 使用函数参数指定文件密钥.
# app.py
from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post('/upload')
def upload_file(file: UploadFile = File(...)):
return {'name': file.filename}
表格提交永久链接
我们想要访问如下所示定义的文本表单字段并回显该值.
<input name='city' type='text'>
烧瓶
Flask 允许通过请求对象访问表单字段.
# app.py
from flask import Flask, request
app = Flask(__name__)
@app.route('/submit', methods=['POST'])
def echo():
city = request.form.get('city')
return {'city': city}
快速API:
我们使用函数参数来定义表单字段的键和数据类型.
# app.py
from fastapi import FastAPI, Form
app = FastAPI()
@app.post('/submit')
def echo(city: str = Form(...)):
return {'city': city}
我们还可以使表单字段可选,如下所示
from typing import Optional
@app.post('/submit')
def echo(city: Optional[str] = Form(None)):
return {'city': city}
同样,我们可以为表单字段设置一个默认值,如下所示.
@app.post('/submit')
def echo(city: Optional[str] = Form('Paris')):
return {'city': city}
饼干永久链接
我们想要访问一个名为 name
从请求.
烧瓶
Flask 允许通过请求对象访问 cookie.
# app.py
from flask import Flask, request
app = Flask(__name__)
@app.route('/profile')
def profile():
name = request.cookies.get('name')
return {'name': name}
快速API:
我们使用参数来定义 cookie 的键.
# app.py
from fastapi import FastAPI, Cookie
app = FastAPI()
@app.get('/profile')
def profile(name = Cookie(None)):
return {'name': name}
模块化视图永久链接
我们希望将视图从单个 app.py 分解为单独的文件.
- app.py
- views
- user.py
烧瓶:
在 Flask 中,我们使用一个称为蓝图的概念来管理它。 我们首先为用户视图创建一个蓝图,如下所示:
# views/user.py
from flask import Blueprint
user_blueprint = Blueprint('user', __name__)
@user_blueprint.route('/users')
def list_users():
return {'users': ['a', 'b', 'c']}
然后,这个视图注册在主 app.py
文件.
# app.py
from flask import Flask
from views.user import user_blueprint
app = Flask(__name__)
app.register_blueprint(user_blueprint)
快速API:
在 FastAPI 中,蓝图的等价物称为路由器。 首先,我们创建一个用户路由器:
# routers/user.py
from fastapi import APIRouter
router = APIRouter()
@router.get('/users')
def list_users():
return {'users': ['a', 'b', 'c']}
然后,我们将此路由器附加到主应用程序对象:
# app.py
from fastapi import FastAPI
from routers import user
app = FastAPI()
app.include_router(user.router)
数据验证永久链接
烧瓶
Flask 不提供任何开箱即用的输入数据验证功能。 通常的做法是编写自定义验证逻辑或使用库,例如 棉花糖 或者 Pydantic.
快速API:
FastAPI 将 pydantic 包装到其框架中,并通过简单地使用 pydantic 模式和 python 类型提示的组合来允许数据验证.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str
age: int
@app.post('/users')
def save_user(user: User):
return {'name': user.name,
'age': user.age}
此代码将执行自动验证以确保 name
是一个字符串并且 age
是一个整数。 如果发送任何其他数据类型,它会自动生成带有相关消息的验证错误.
以下是一些常见用例的 pydantic 模式示例.
示例 1:键值对永久链接
{
"name": "Isaac",
"age": 60
}
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
示例 2:事物的收集永久链接
{
"series": ["GOT", "Dark", "Mr. Robot"]
}
from pydantic import BaseModel
from typing import List
class Metadata(BaseModel):
series: List[str]
示例 3:嵌套对象永久链接
{
"users": [
{
"name": "xyz",
"age": 25
},
{
"name": "abc",
"age": 30
}
],
"group": "Group A"
}
from pydantic import BaseModel
from typing import List
class User(BaseModel):
name: str
age: int
class UserGroup(BaseModel):
users: List[User]
group: str
您可以从以下位置了解有关 Python 类型提示的更多信息 这里.
自动文档永久链接
烧瓶
Flask 不提供任何用于文档生成的内置功能。 有一些扩展,例如 烧瓶招摇 或者 烧瓶宁静的 填补这一空白,但工作流程相对复杂.
快速API:
FastAPI 自动生成一个交互式 swagger 文档端点 /docs
和参考文档 /redoc
.
例如,假设我们有一个下面给出的简单视图,它与用户搜索的内容相呼应.
# app.py
from fastapi import FastAPI
app = FastAPI()
@app.get('/search')
def search(q: str):
return {'query': q}
招摇文档永久链接
如果您运行服务器并转到端点 http://127.0.0.1:8000/docs
, 你会得到一个自动生成的招摇文档.
您可以从浏览器本身以交互方式试用 API.
ReDoc 文档永久链接
除了招摇,如果你去端点 http://127.0.0.01:8000/redoc
, 您将获得自动生成的参考文档。 有关于参数、请求格式、响应格式和状态码的信息.
跨域资源共享(CORS)永久链接
烧瓶
Flask 不提供开箱即用的 CORS 支持。 我们需要使用扩展名,例如 烧瓶-cors 配置 CORS 如下所示.
# app.py
from flask import Flask
from flask_cors import CORS
app_ = Flask(__name__)
CORS(app_)
快速API:
FastAPI 提供了一个 内置中间件 处理 CORS。 我们在下面展示了一个 CORS 示例,我们允许任何来源访问我们的 API.
# app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
结论永久链接
因此,FastAPI 是 Flask 的绝佳替代品,用于构建具有最佳实践的健壮 API。您可以参考 文件 了解更多.