FastAPI入门教程

本文将会通过与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
# app.py
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
# app.py
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,示例命令如下:

1
gunicorn app:app

而FastAPI使用uvicorn,示例命令如下:

1
uvicorn app:app

当然,在实际部署时还可指定端口(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
# 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}

FastAPI的代码如下:

1
2
3
4
5
6
7
8
# app.py
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
# 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}

FastAPI代码如下:

1
2
3
4
5
6
7
# app.py
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
# app.py

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
# app.py
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
# 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)

在FastAPI中,与蓝图等价的实现形式为router,首先需创建一个user router如下:

1
2
3
4
5
6
7
# routers/user.py
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
# app.py
from fastapi import FastAPI
from routers import user

app = FastAPI()
app.include_router(user.router)

数据校验

Flask本身并没有提供数据校验功能,可使用marshmalllowpydantic模块来辅助实现。

而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-swaggerflask-restful模块来辅助实现。

FastAPI可生成自动化文档,文档风格包括swagger和redoc,其中swagger对应路径为/docs,redoc对应路径为/redoc,如下所示:

Swagger风格

swagger风格文档
查看某个接口

ReDoc风格

ReDoc风格文档

CORS

Flask本身不支持CORS,可使用flask-cors模块辅助实现,代码如下:

1
2
3
4
5
6
7
# app.py

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
# 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=["*"],
)

总结

本文通过与Flask的对比,来介绍FastAPI的基础使用。如果读者想进一步了解FastAPI的高级使用方法,可参考FastAPI官方文档

感谢阅读~

欢迎关注我的公众号NLP奇幻之旅,原创技术文章第一时间推送。

欢迎关注我的知识星球“自然语言处理奇幻之旅”,笔者正在努力构建自己的技术社区。


FastAPI入门教程
https://percent4.github.io/FastAPI入门教程/
作者
Jclian91
发布于
2024年1月10日
许可协议