FastAPI 是一个相对较新的 Python Web 框架,号称是可用的最快的 Python 框架之一。在本文中,我将在我短暂的使用过程中讨论该框架的优缺点。我还将提供一些示例和解决方案,以尽量减少缺点。
好的
1.它确实是FastAPI
FastAPIFlask当我们将它与其他主要 Python 框架(如和 )进行比较时,它的速度相当快Django。以下来自Techempower的分数图表显示了这些框架之间的性能差异有多大。
我自己也做了一个小测试,看看哪个框架最快,结果其实很有趣。对于此测试,我为所有 3 个框架设置了基本的“Hello world”API。我通过调用 API 来测试响应时间并获取平均响应时间。结果可以分为两种情况:
A。 服务器启动后第一次调用的平均时间
b. 第一次通话后连续通话的平均时间
Django并且FastAPI在第一次 API 调用中响应速度比平常慢。Flask在所有 API 调用期间始终保持一致,但比其他两个慢得多。
所有 3 个 API 所花费的平均时间如下所示:
框架 | 案例一 | 情况b |
FastAPI | 17 毫秒 | 6.2毫秒 |
Django | 517.2 毫秒 | 5.834 毫秒 |
Flask | 507.2 毫秒 | 508.9 毫秒 |
这里值得注意的一件有趣的事情是,在第一次调用后,Django 实际上比 FastAPI 执行得更快一些。但在某些场景(例如无服务器环境)中,Django 的高首次调用和启动时间可能会成为一个问题。需要注意的是,测量是在特定环境下用很少的数据完成的,而且我对 Flask 和 Django 的经验非常有限,所以结果可能会因你而异。
2.支持异步代码
FastAPI 最令人兴奋的功能是它支持使用 Python 关键字开箱即用的异步代码async/await。下面是一个从 Reddit 异步获取数据的 API 示例。(示例参考:Scott Robinson 的 Python async/await 教程)
app = FastAPI()
async def get_json(client: ClientSession, url: str) -> bytes:
async with client.get(url) as response:
assert response.status == 200
return await response.read()
async def get_reddit_top(subreddit: str, client: ClientSession, data: dict):
data1 = await get_json(client, 'https://www.reddit.com/r/' + subreddit + '/top.json?sort=top&t=day&limit=5')
j = json.loads(data1.decode('utf-8'))
subreddit_data = []
for i in j['data']['children']:
score = i['data']['score']
title = i['data']['title']
link = i['data']['url']
print(str(score) + ': ' + title + ' (' + link + ')')
subreddit_data.append(str(score) + ': ' + title + ' (' + link + ')')
data[subreddit] = subreddit_data
print('DONE:', subreddit + '\n')
@app.get("/")
async def get_reddit_data_api() -> dict:
start_time: float = time.time()
client: ClientSession = aiohttp.ClientSession()
data: dict = {}
await asyncio.gather(
get_reddit_top('python', client, data),
get_reddit_top('programming', client, data),
get_reddit_top('compsci', client, data),
)
await client.close()
print("Got reddit data in ---" + str(time.time() - start_time) + "seconds ---")
return data
异步代码的神奇之处在于,由于协程get_reddit_top同时运行,API 的执行时间与串行运行的执行时间相比显着减少。
3. 开发时间极短
要创建基本的“Hello world”API,框架需要以下代码行数(考虑整个项目):
框架 | 代码行数 |
FastAPI | 8行 |
Flask | 7行 |
我没有考虑过 Django,因为我认为它的结构与其他两个不同。
如果您想扩展 FastApi 应用程序,其工作量也与 Flask 类似。两者都通过 Flask 中的 Blueprint 和 FastAPI 中的 Router 实现了模块化概念。所以,我想说 Flask 和 FastAPI 的开发时间非常相似。
4. 测试方便
测试 FastAPI 端点非常简单,可以使用FastAPI 提供的TestClient来完成。这使得测试驱动开发(TDD)变得非常容易。
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
您可以轻松模拟 API 定义函数中的服务调用或代码,read_main并使用 TestClient 对其进行测试。
5. 无缝的中央异常处理
要在 FastAPI 中进行异常处理,您只需使用@app.exception_handler注释或app.add_exception_handler函数来注册 an 的响应Exception,它将由 FastAPI 处理。
app = FastAPI()
@app.exception_handler(SomeException)
async def http_exception_handler(request: Request, exc: SomeException) -> PlainTextResponse:
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
async def request_exception_handler(request: Request, exc: SomeOtherException) -> PlainTextResponse:
return PlainTextResponse(str(exc.detail),status_code=exc.status_code)
app.add_exception_handler(exc_class_or_status_code=SomeOtherException,
handler=request_exception_handler)
6. 优秀的文档
FastAPI 有非常广泛且示例丰富的文档,这使事情变得更容易。如果您需要查找有关 FastAPI 的信息,通常无需到其他地方查找。
7. 轻松部署
您可以使用 FastAPI 提供的docker 镜像通过 Docker 轻松部署 FastAPI 应用程序。您还可以使用Mangum将其部署到 AWS Lamdba 。
坏处
1.主文件拥挤
在 FastAPI 中,一切都与FastAPI app. 因此,您的main.py文件很容易变得非常拥挤。这是一个例子。
app = FastAPI()
app.include_router(users.router)
app.include_router(items.router)
app.include_router(shops.router)
app.include_router(other.router)
@app.exception_handler(SomeException)
async def http_exception_handler(request: Request, exc: SomeException) -> PlainTextResponse:
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(SomeOtherException)
async def http_exception_handler(request: Request, exc: SomeOtherException) -> PlainTextResponse:
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
现在假设您有 10 个路由器和 20 个异常需要处理,main.py那么该文件可能会变得非常难以维护。幸运的是,这个问题很容易解决。
app = FastAPI()
include_routers(app);
add_exception_handlers(app);
include_routers并add_exception_handlers可以保存在单独的文件中。
2. 依赖注入中没有单例
singleton根据此Github 线程, FastAPI 中的依赖注入不支持实例,但它支持每个 HTTP 请求的单个实例。您要么必须自己创建单例类,要么使用不同的 DI 库。
丑陋的
请求验证
我在使用 FastAPI 时最糟糕的经历是处理请求验证。它使用来自 的验证Pydantic,据我所知,没有直接的方法将验证消息从验证点传递到响应。Pydantic您可以凑合使用通过传递下来的任何消息RequestValidationError或编写自定义验证器。例如,
app = FastAPI()
class SomeDto(BaseModel):
data: str = Field(min_length=1, description="Minimum length must be greater than 1",
title="Minimum length must be greater than 1")
@app.post(path="/")
async def get_response(request: SomeDto):
return "some response"
@app.exception_handler(RequestValidationError)
async def handle_error(request: Request, exc: RequestValidationError) -> PlainTextResponse:
return PlainTextResponse(str(exc.errors()), status_code=400)
exc.errors()返回验证违规列表,其中包含来自 的硬编码消息Pydantic。FastAPI我发现没有办法从和的文档中更改它Pydantic。甚至description和title参数值也会丢失。