本文将会通过与Flask的对比,来介绍FastAPI的基础使用。熟悉Flask的读者,阅读本文后应当不难掌握FastAPI。
Flask
作为Python语言中的老牌Web框架,已经被应用于大量的Python
Web开发项目,其使用简洁,支持工具众多,工具丰富,社区活跃,是Python
Web框架中的佼佼者之一。
而近来,FastAPI
的出众表现,已使得其越来越受到众多开发者的关注,成为Web开发主流框架之一。FastAPI优势如下:
快速:可与 NodeJS 和 Go 并肩的极高性能(归功于
Starlette 和 Pydantic)。最快的 Python web 框架之一。
高效编码:提高功能开发速度约 200% 至
300%。
更少 bug:减少约 40%
的人为(开发者)导致错误。
智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
简单:设计的易于使用和学习,阅读文档的时间更短。
简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug
更少。
健壮:生产可用级别的代码。还有自动生成的交互式文档。
标准化:基于(并完全兼容)API
的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
下面将会结合Flask
的使用作为对比,来介绍FastAPI
,作为FastAPI
的入门教程。
本文使用的两个Web框架版本如下:
1 2
| fastapi==0.101.1 Flask==2.3.3
|
Hello World
Flask的代码如下:
1 2 3 4 5 6 7 8 9 10 11
| from flask import Flask
app = Flask(__name__)
@app.route('/') def home(): return 'hello world'
if __name__ == '__main__': app.run()
|
FastAPI的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import uvicorn from fastapi import FastAPI
app = FastAPI()
@app.get('/') def home(): return 'hello world'
if __name__ == '__main__': uvicorn.run(app)
|
两者的代码差别不多,运行时Flask的默认端口为5000,FastAPI的端口为8000,使用curl命令请求(FastAPI),返回结果如下:
1 2
| $ curl localhost:8000/ "hello world"
|
在部署生产代码时,Flask使用gunicorn
,示例命令如下:
而FastAPI使用uvicorn
,示例命令如下:
当然,在实际部署时还可指定端口(port)、worker数量、最大连接数等,本文不再详述。
HTTP方法
常见的HTTP请求方法有GET, POST, PUT, PATCH, DELETE等。
以POST方法为例,Flask的代码如下:
1 2 3
| @app.route('/', methods=['POST']) def example(): ...
|
FastAPI的代码如下:
1 2 3
| @app.post('/') def example(): ...
|
其它HTTP请求方法的使用方法如下:
1 2 3 4
| @app.get('/') @app.put('/') @app.patch('/') @app.delete('/')
|
URL 变量
我们想从URL中获取user id,比如/users/1
,然后将user
id返回给用户。
Flask的代码如下:
1 2 3
| @app.route('/users/<int:user_id>') def get_user_details(user_id): return {'user_id': user_id}
|
FastAPI的代码如下:
1 2 3
| @app.get('/users/{user_id}') def get_user_details(user_id: int): return {'user_id': user_id}
|
使用curl命令模拟请求如下:
1 2
| $ curl localhost:8000/users/2 {"user_id":2}
|
查询字符串
我们希望允许用户在URL中使用查询字符串 ?q=abc
来指定搜索词。
Flask的代码如下:
1 2 3 4 5 6
| from flask import request
@app.route('/search') def search(): query = request.args.get('q') return {'query': query}
|
FastAPI的代码如下:
1 2 3
| @app.get('/search') def search(q: str): return {'query': q}
|
使用curl命令模拟请求如下:
1 2
| $ curl 'localhost:8000/search?q=abcde' {"query":"abcde"}
|
如果要指定多个搜索词,可以&符号隔开,比如:
?name=Jack&id=1
JSON Post 请求
我们希望发送一个带有text参数JSON POST请求,返回其小写形式。
1 2 3 4 5
| # Request {"text": "HELLO"}
# Response {"text": "hello"}
|
Flask代码如下:
1 2 3 4 5 6
| from flask import request
@app.route('/lowercase', methods=['POST']) def lower_case(): text = request.json.get('text') return {'text': text.lower()}
|
FastAPI代码如下:
1 2 3 4 5 6
| from typing import Dict
@app.post('/lowercase') def lower_case(json_data: Dict): text = json_data.get('text') return {'text': text.lower()}
|
注意:FastAPI还有更优雅的处理方式,那就是引入Pydantic schema
,它可以将JSON格式数据映射为schema对象,同时对该对象中的数据类型进行校验,如校验不通过,则会返回自动生成的校验错误。
FastAPI更优雅的代码如下:
1 2 3 4 5 6
| class Sentence(BaseModel): text: str
@app.post('/lowercase') def lower_case(sentence: Sentence): return {'text': sentence.text.lower()}
|
使用curl命令模拟请求如下:
1 2 3 4 5 6
| $ curl --location 'localhost:8000/lowercase' \ --header 'Content-Type: application/json' \ --data '{ "text": "HELLO" }' {"text":"hello"}
|
在请求时,如果text对应的value为数字时,FastAPI会自动将数字转化为字符串,不会报错;如果text对应的value为null时,FastAPI将会报错,信息如下:
1 2 3 4 5 6 7 8 9 10 11 12
| { "detail": [ { "loc": [ "body", "text" ], "msg": "none is not an allowed value", "type": "type_error.none.not_allowed" } ] }
|
文件上传
我们来创建一个上传文件的API,返回上传文件的文件名。
Flask代码如下:
1 2 3 4 5 6 7 8 9
|
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}
|
FastAPI的代码如下:
1 2 3 4 5 6 7 8
| from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post('/upload') def upload_file(file: UploadFile = File(...)): return {'name': file.filename}
|
使用Postman模拟请求如下:
$ curl --location 'localhost:8000/upload' \
--header 'Content-Type: multipart/form-data' \
--form 'file=@"/Users/admin/Documents/test.pdf"'
{"name": "test.pdf"}
表单提交
我们希望从表单中获取text类型的变量,并返回它的值,如下:
1
| <input name='city' type='text'>
|
Flask代码如下:
1 2 3 4 5 6 7 8 9
|
from flask import Flask, request app = Flask(__name__)
@app.route('/submit', methods=['POST']) def echo(): city = request.form.get('city') return {'city': city}
|
FastAPI代码如下:
1 2 3 4 5 6 7
| from fastapi import FastAPI, Form app = FastAPI()
@app.post('/submit') def echo(city: str = Form(...)): return {'city': city}
|
使用curl命令模拟请求如下:
1 2 3
| $ curl --location 'localhost:8000/submit' \ --form 'city="shanghai"' {"city":"shanghai"}
|
Cookies
我们希望从请求的cookie中获取name字段的值。
Flask代码如下:
1 2 3 4 5 6 7 8 9
|
from flask import Flask, request app = Flask(__name__)
@app.route('/profile') def profile(): name = request.cookies.get('name') return {'name': name}
|
FastAPI代码如下:
1 2 3 4 5 6 7
| from fastapi import FastAPI, Cookie app = FastAPI()
@app.get('/profile') def profile(name=Cookie(None)): return {'name': name}
|
模块化视图
我们希望将单个的app.py文件分解成视图(多个独立的文件),如下:
1 2 3
| - app.py - views - user.py
|
在Flask中,我们可以使用蓝图(Blueprint)来管理,代码如下:
user.py
1 2 3 4 5 6 7
| 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)
在FastAPI中,与蓝图等价的实现形式为router,首先需创建一个user
router如下:
1 2 3 4 5 6 7
| from fastapi import APIRouter router = APIRouter()
@router.get('/users') def list_users(): return {'users': ['a', 'b', 'c']}
|
将其加入app.py中,代码如下:
1 2 3 4 5 6
| from fastapi import FastAPI from routers import user
app = FastAPI() app.include_router(user.router)
|
数据校验
Flask本身并没有提供数据校验功能,可使用marshmalllow
或pydantic
模块来辅助实现。
而FastAPI在其框架中已包装pydantic
模块,可轻松实现数据校验。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import uvicorn 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}
if __name__ == '__main__': uvicorn.run(app)
|
关于pydantic
的schema与JSON格式对应关系,在此给出三个例子:
例子1
键值对(key-value pairs)
1 2 3 4
| { "name": "Isaac", "age": 60 }
|
1 2 3 4 5
| from pydantic import BaseModel
class User(BaseModel): name: str age: int
|
例子2
列表
1 2 3
| { "series": ["GOT", "Dark", "Mr. Robot"] }
|
1 2 3 4 5
| from pydantic import BaseModel from typing import List
class Metadata(BaseModel): series: List[str]
|
例子3
嵌套对象
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "users": [ { "name": "xyz", "age": 25 }, { "name": "abc", "age": 30 } ], "group": "Group A" }
|
1 2 3 4 5 6 7 8 9 10
| from pydantic import BaseModel from typing import List
class User(BaseModel): name: str age: int
class UserGroup(BaseModel): users: List[User] group: str
|
自动化文档
Flask并没有提供内置的自动化文档生成,可使用flask-swagger
或flask-restful
模块来辅助实现。
FastAPI可生成自动化文档,文档风格包括swagger和redoc,其中swagger对应路径为/docs,redoc对应路径为/redoc,如下所示:
Swagger风格
ReDoc风格
CORS
Flask本身不支持CORS,可使用flask-cors
模块辅助实现,代码如下:
1 2 3 4 5 6 7
|
from flask import Flask from flask_cors import CORS
app_ = Flask(__name__) CORS(app_)
|
FastAPI提供了内置的中间件来处理CORS,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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=["*"], )
|
总结
本文通过与Flask的对比,来介绍FastAPI的基础使用。如果读者想进一步了解FastAPI的高级使用方法,可参考FastAPI官方文档。
感谢阅读~
欢迎关注我的公众号
NLP奇幻之旅,原创技术文章第一时间推送。
欢迎关注我的知识星球“自然语言处理奇幻之旅”,笔者正在努力构建自己的技术社区。