Python Web 开发:FastAPI vs Flask 入门介绍
Python 作为一门功能强大且易于学习的编程语言,在 Web 开发领域占据着重要的地位。得益于其丰富的生态系统和众多的 Web 框架,开发者可以高效地构建从简单的网站到复杂的 Web 应用和 API 服务。在众多 Python Web 框架中,Flask 和 FastAPI 是近些年备受关注的两个佼佼者。
Flask 以其轻量级、灵活的特性而闻名,被誉为“微框架”的典范。它提供构建 Web 应用所需的基本工具,但将大量决策权留给开发者,允许他们根据项目需求自由选择扩展和库。
FastAPI 则是一个相对较新的框架,它基于现代 Python 特性(如类型提示)构建,旨在提供极高的性能、优秀的开发者体验和自动化的API文档。它迅速崛起,成为构建高性能 API 的热门选择。
对于初入 Python Web 开发领域的初学者来说,了解这两个框架的特点、优势以及如何选择,是迈出第一步的关键。本文将深入浅出地介绍 Flask 和 FastAPI,通过基本示例代码展示它们的用法,并进行详细对比,帮助你做出明智的选择。
第一部分:Flask – 灵活的微框架经典
Flask 是由 Armin Ronacher 创建的一个 Python Web 微框架。它的核心理念是保持核心简单、易于扩展。Flask 不会替你做太多决定,比如使用哪个数据库、模板引擎或认证库。这使得 Flask 极度灵活,你可以根据项目需求自由搭配各种库和工具。
1. Flask 的核心特性与理念
- 微框架(Microframework): Flask 自身只提供处理 HTTP 请求和响应、路由、模板渲染等核心功能。它没有内置数据库抽象层、表单验证工具等。
- 简单易学: Flask 的 API 设计直观,文档清晰,非常适合初学者快速上手。
- 灵活性: 开发者可以自由选择使用哪些第三方库来完成数据库操作、用户认证、表单处理等任务。
- 基于 WSGI: Flask 基于 Web Server Gateway Interface (WSGI) 标准构建,可以运行在任何支持 WSGI 的 Web 服务器上(如 Gunicorn, uWSGI)。
- 强大的社区和生态系统: Flask 拥有庞大且活跃的社区,有大量的第三方扩展(Flask-Extensions)可供使用,覆盖了数据库集成、用户认证、API 构建、管理面板等各种需求。
2. Flask 的基本用法:入门示例
让我们通过一个简单的“Hello, World”示例来了解 Flask 的基本用法。
首先,确保你已经安装了 Python。然后,通过 pip 安装 Flask:
bash
pip install Flask
接下来,创建一个名为 app.py
的文件,并输入以下代码:
“`python
app.py
from flask import Flask, request, jsonify
创建一个 Flask 应用实例
app = Flask(name)
定义一个路由,映射到根 URL “/”
@app.route(‘/’)
def hello_world():
return ‘Hello, World!’
定义一个带参数的路由
@app.route(‘/greet/
def greet(name):
# 返回一个简单的 HTML 字符串
return f’
Hello, {name}!
‘
定义一个处理 POST 请求的 API 路由
@app.route(‘/api/data’, methods=[‘POST’])
def process_data():
# 获取请求体中的 JSON 数据
data = request.get_json()
if data:
# 处理数据(这里只是简单地返回接收到的数据)
processed_data = {“received”: data, “status”: “success”}
# 返回 JSON 响应
return jsonify(processed_data), 200
else:
return jsonify({“error”: “No JSON data received”}), 400
运行 Flask 应用
if name == ‘main‘:
# debug=True 模式下,当代码修改时服务器会自动重启,并提供详细的错误信息
app.run(debug=True)
“`
运行这个应用:
bash
python app.py
你会看到类似以下的输出:
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: ...
现在,你可以打开浏览器访问 http://127.0.0.1:5000/
,会看到 “Hello, World!”。
访问 http://127.0.0.1:5000/greet/Alice
,会看到 “Hello, Alice!”。
你也可以使用工具(如 Postman 或 curl)向 http://127.0.0.1:5000/api/data
发送 POST 请求,请求体为 JSON 格式,例如 {"name": "Bob", "age": 30}
,服务器会返回相应的 JSON 响应。
3. Flask 的核心概念详解
- Flask 应用实例 (
Flask(__name__)
): 这是 Flask 应用的入口。__name__
是当前模块的名称,Flask 用它来确定应用根目录,方便查找模板、静态文件等。 - 路由 (
@app.route('/path')
): 使用装饰器@app.route()
将一个 URL 路径映射到一个 Python 函数。当用户访问该 URL 时,对应的函数会被执行,其返回值作为 HTTP 响应发送回客户端。你可以指定请求方法(如methods=['POST', 'GET']
)。 - 请求对象 (
request
): 当一个请求到来时,Flask 会创建一个request
对象,包含了所有请求相关的信息,如请求方法、URL 参数、表单数据、JSON 数据、头部信息等。你需要从flask
模块导入request
对象才能使用。 - 响应对象 (
response
): 路由函数返回的值会被 Flask 转换成一个response
对象。这个返回值可以是一个简单的字符串、一个 HTML 字符串、一个模板渲染结果,或者使用jsonify()
创建的 JSON 响应。你可以返回响应体、状态码和头部信息(例如return 'OK', 200, {'Content-Type': 'text/plain'}
)。 - 模板引擎 (Jinja2): 虽然基本示例返回了字符串,但在实际 Web 开发中,通常使用模板引擎生成动态 HTML。Flask 默认集成了 Jinja2 模板引擎。你可以创建
.html
文件,使用 Jinja2 语法嵌入 Python 变量和控制结构,然后在路由函数中使用render_template()
函数渲染模板。 - 上下文 (Context): Flask 有两种上下文:应用上下文 (Application Context) 和请求上下文 (Request Context)。
current_app
和g
属于应用上下文,request
和session
属于请求上下文。这些上下文使得在请求处理过程中可以方便地访问与当前请求或应用相关的信息,即使在当前函数没有直接传入request
对象的情况下。理解上下文对于使用 Flask 扩展和编写可维护的应用至关重要。 - 扩展 (Flask-Extensions): Flask 的强大很大程度上依赖于其丰富的扩展生态。常见的扩展有:
Flask-SQLAlchemy
: 集成 SQLAlchemy ORM 进行数据库操作。Flask-Migrate
: 使用 Alembic 进行数据库迁移。Flask-WTF
: 集成 WTForms 进行表单处理和验证。Flask-Login
: 处理用户会话管理和认证。Flask-RESTful
/Flask-RESTx
: 帮助构建 RESTful API。Flask-Mail
: 发送邮件。
4. Flask 的优势与劣势
优势:
- 易于学习和上手: API 简单直观,核心概念少。
- 极度灵活: 不绑定特定的技术栈,可以自由选择组件。
- 成熟稳定: 经过长时间的考验,社区庞大,资源丰富。
- 庞大的生态系统: 大量高质量的第三方扩展几乎可以满足所有常见需求。
- 适合小型项目或简单的 Web 服务: 对于不需要太多“魔术”的小型应用,Flask 是一个快速启动的好选择。
劣势:
- “微”意味着你需要做更多决策: 对于初学者来说,选择合适的扩展并组合起来构建一个完整的应用可能需要额外的学习成本。
- 缺乏内置的通用功能: 数据库、认证、表单等常见功能都需要通过扩展实现,这可能导致依赖管理变得复杂。
- 在构建大型应用时需要更强的架构设计能力: 随着项目增长,如果缺乏良好的组织结构,代码可能变得难以维护。
- 原生不支持异步: Flask 基于 WSGI,是一个同步框架。虽然可以通过 eventlet 或 gevent 等库实现协程,或者结合 ASGI 网关运行,但其核心设计不是为异步 I/O 优化的。
- 缺乏内置的数据校验和序列化: 处理 JSON 数据时,通常需要手动校验或使用第三方库,没有内置的强大支持。
第二部分:FastAPI – 现代高性能框架
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 Web 框架,使用标准的 Python 类型提示,并基于标准的 OpenAPI 和 JSON Schema。它由 Sebastián Ramírez 开发,并迅速获得了广泛关注。
FastAPI 建立在两个主要组件之上:
- Starlette: 一个轻量级、高性能的 ASGI 框架,负责处理网络部分(请求、响应、路由等)。Starlette 提供了卓越的异步能力。
- Pydantic: 一个用于数据校验、序列化和模型定义的库,基于 Python 类型提示。FastAPI 利用 Pydantic 来自动处理请求数据的校验和响应数据的序列化。
FastAPI 的设计目标是提供极高的性能,同时提供优秀的开发者体验,并自动生成清晰的 API 文档。
1. FastAPI 的核心特性与理念
- 极高的性能: 基于 Starlette (ASGI) 和 Pydantic 构建,性能可与 NodeJS 和 Go 等框架媲美(在 Uvicorn 等 ASGI 服务器下)。
- 快速开发: 自动化的数据校验、序列化和文档生成极大地提高了开发效率。
- 强大的类型提示支持: 充分利用 Python 3.6+ 的类型提示,不仅提高了代码可读性和可维护性,还使得 FastAPI 能够在运行时进行数据校验、序列化和生成文档。
- 自动生成 API 文档: 基于 OpenAPI (以前称为 Swagger) 标准,FastAPI 会自动生成交互式 API 文档(Swagger UI 和 ReDoc)。
- 数据校验和序列化: 使用 Pydantic,可以轻松定义数据模型,FastAPI 会自动校验请求数据,并在返回响应时序列化数据。
- 依赖注入系统: 内置强大的依赖注入系统,使得组织代码、管理资源(如数据库连接、认证信息)变得非常方便和可测试。
- 异步支持 (
async
/await
): 原生支持异步请求处理,特别适合处理 I/O 密集型任务(如数据库查询、外部 API 调用),可以显著提高并发性能。 - 基于 ASGI: 基于 Asynchronous Server Gateway Interface (ASGI) 标准构建,可以运行在任何支持 ASGI 的服务器上(如 uvicorn, hypercorn, daphne)。
2. FastAPI 的基本用法:入门示例
让我们通过一个简单的“Hello, World”示例以及一个带数据校验的 API 示例来了解 FastAPI 的基本用法。
首先,确保你已经安装了 Python。然后,通过 pip 安装 FastAPI 以及一个 ASGI 服务器(推荐使用 uvicorn):
bash
pip install fastapi uvicorn[standard]
接下来,创建一个名为 main.py
的文件,并输入以下代码:
“`python
main.py
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
创建一个 FastAPI 应用实例
app = FastAPI()
定义一个 Pydantic 模型用于请求体和响应
class Item(BaseModel):
name: str
price: float
is_offer: Optional[bool] = None # Optional 表示该字段可选,并设置默认值
定义一个根路由
@app.get(“/”)
def read_root():
return {“Hello”: “World”}
定义一个带路径参数的路由
{item_id} 是路径参数
@app.get(“/items/{item_id}”)
def read_item(item_id: int, q: Optional[str] = None):
# FastAPI 会自动将 item_id 转换成 int 类型
# q 是一个可选的查询参数
return {“item_id”: item_id, “q”: q}
定义一个处理 POST 请求的 API 路由,带请求体和路径参数
@app.post(“/items/{item_id}”)
def create_item(item_id: int, item: Item):
# item_id 是路径参数 (int)
# item 是请求体,FastAPI 会自动将 JSON 请求体转换为 Item 对象,并进行数据校验
return {“item_id”: item_id, “item”: item}
另一个 POST 示例,只接收请求体
@app.post(“/process/”)
def process_data(data: Item):
# 直接接收一个 Item 对象作为请求体
return {“processed”: data, “message”: “Data received and processed”}
“`
运行这个应用:
bash
uvicorn main:app --reload
main:app
指的是main.py
文件中的app
对象。--reload
参数使得在你修改代码时服务器会自动重启,方便开发。
你会看到类似以下的输出:
INFO: Will watch for changes in these directories: ['/path/to/your/project']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [xxxxx]
INFO: Started server running in loop asyncio
INFO: Waiting for application startup.
INFO: Application startup complete.
现在,你可以:
- 打开浏览器访问
http://127.0.0.1:8000/
,会看到{"Hello":"World"}
。 - 打开浏览器访问
http://127.0.0.1:8000/items/5
,会看到{"item_id":5,"q":null}
。 - 打开浏览器访问
http://127.0.0.1:8000/items/5?q=somequery
,会看到{"item_id":5,"q":"somequery"}
。 - 访问自动文档: 打开浏览器访问
http://127.0.0.1:8000/docs
,你会看到交互式的 Swagger UI 文档。 - 你也可以使用工具(如 Postman 或 curl)向
http://127.0.0.1:8000/items/10
发送 POST 请求,请求体为 JSON 格式,例如{"name": "Laptop", "price": 1200.50, "is_offer": true}
。FastAPI 会自动校验数据。如果数据格式不正确,它会返回一个清晰的错误信息。
3. FastAPI 的核心概念详解
- FastAPI 应用实例 (
FastAPI()
): 这是 FastAPI 应用的入口,类似 Flask 的Flask(__name__)
。 - 路径操作 (Path Operations): FastAPI 不使用
@app.route()
装饰器,而是为不同的 HTTP 方法提供了专门的装饰器,如@app.get()
,@app.post()
,@app.put()
,@app.delete()
等。这些装饰器定义了处理特定 URL 路径和 HTTP 方法的函数。 - 类型提示 (Type Hints): FastAPI 广泛使用 Python 的类型提示。通过函数参数的类型提示,FastAPI 可以自动识别路径参数、查询参数、请求体、Header、Cookie 等,并进行数据校验、转换。例如,
item_id: int
告诉 FastAPI 期望一个整数类型的item_id
。 - Pydantic (
BaseModel
): 用于定义数据模型。通过继承pydantic.BaseModel
创建类,并使用类型提示定义字段。FastAPI 会自动将 JSON 请求体转换为 Pydantic 模型实例,并根据模型定义进行数据校验。校验失败会自动返回 422 错误。同时,Pydantic 模型实例在返回给客户端时会自动序列化为 JSON。 - 请求数据处理:
- 路径参数 (Path Parameters): 在 URL 路径中用
{param_name}
定义,函数参数名需与{param_name}
匹配,并使用类型提示。 - 查询参数 (Query Parameters): 不在 URL 路径中定义,直接作为函数参数,并使用类型提示。可以使用
Optional
和默认值。 - 请求体 (Request Body): 通常使用 Pydantic 模型作为函数参数。FastAPI 会自动读取请求体,校验并转换为模型实例。
- 路径参数 (Path Parameters): 在 URL 路径中用
- 依赖注入 (Dependency Injection): 使用
Depends()
函数在路径操作函数中声明依赖。依赖可以是一个函数,该函数可以返回某个对象(如数据库连接)或执行某些校验(如用户认证)。FastAPI 会自动解析和执行依赖,并将结果注入到路径操作函数中。这极大地提高了代码的模块化和可测试性。 - 异步 (
async def
): 使用async def
定义路径操作函数,可以在函数内部使用await
调用异步库(如asyncio
,aiohttp
,databases
)。这使得 FastAPI 在处理大量并发连接和 I/O 密集型任务时表现出色。
4. FastAPI 的优势与劣势
优势:
- 高性能: 基于 ASGI 异步框架,处理并发请求能力强,特别适合构建高性能 API 服务。
- 极高的开发效率: 自动化的数据校验、序列化和文档生成减少了大量重复工作。
- 优秀的开发者体验: 利用类型提示提供了良好的代码补全、内联错误检查。
- 自动、交互式的 API 文档: Swagger UI 和 ReDoc 文档非常方便开发和测试。
- 强大的数据校验和序列化: 基于 Pydantic,非常方便且功能强大。
- 内置依赖注入系统: 简化了代码组织和测试。
- 原生支持异步: 方便进行异步编程。
劣势:
- 相对较新: 社区和生态系统不如 Flask 或 Django 成熟,但正在迅速壮大。一些特定的需求可能还没有现成的扩展,需要自己实现或寻找其他库。
- 类型提示和 Pydantic 可能有学习曲线: 对于不熟悉现代 Python 类型提示或 Pydantic 的开发者,入门可能需要一些时间。
- 对类型提示的依赖较高: 如果团队不习惯或不遵守类型提示规范,可能会影响 FastAPI 提供的便利性。
- 更侧重于 API 构建: 虽然可以用于构建传统的 Web 应用(配合模板引擎),但其核心设计和功能更倾向于构建 API 服务。
第三部分:FastAPI vs Flask:详细对比
现在我们来更详细地对比这两个框架,从不同维度看看它们的异同。
1. 设计哲学与核心定位
- Flask: 微框架,极简主义。提供核心功能,将选择权交给开发者。更像是一个工具箱,你需要自己挑选和组装工具。适合对框架有高度控制需求、或项目需求非常明确且不复杂的场景。
- FastAPI: 现代框架,高性能,“约定优于配置”与灵活性结合。内置了数据校验、序列化和文档生成等常见功能,同时依然保持了一定的灵活性。更像是一个配备了一些常用自动化工具的专业工作室。特别适合构建高性能的 API 和微服务。
2. 性能
- Flask: 基于 WSGI,默认是同步的。处理高并发请求时性能瓶颈明显(除非结合异步库或 ASGI 网关)。适合 I/O 密集型任务并发性要求不高的场景。
- FastAPI: 基于 ASGI,原生支持异步。在处理 I/O 密集型任务时,性能通常远超 Flask。官方声称性能可与 Go 或 NodeJS 相媲美,这对于需要处理大量并发请求的 API 服务至关重要。
3. 易用性与开发者体验 (DX)
- Flask: API 简单直观,入门代码量少。但在构建复杂应用时,需要手动处理请求数据校验、返回数据序列化等,如果不用扩展,可能会写很多重复代码。没有内置的自动化文档。
- FastAPI: 依赖于类型提示和 Pydantic,初学者可能需要花时间理解。但一旦掌握,开发效率极高。类型提示提供良好的代码补全和静态检查。Pydantic 自动处理数据校验和序列化,减少了样板代码。自动生成的交互式文档是巨大的加分项。
4. 内置功能 (“Batteries Included”)
- Flask: 很少的内置功能。路由、模板(Jinja2)、简单的请求/响应对象是核心。
- FastAPI: 更多的“电池”。内置了基于 Pydantic 的数据校验和序列化,基于 OpenAPI 的自动文档生成,以及强大的依赖注入系统。这些是现代 API 开发中的常见需求。
5. 数据校验与序列化
- Flask: 需要手动进行数据校验,或使用第三方库(如 Marshmallow, Pydantic 自身也可与 Flask 结合使用)。返回 JSON 需要手动序列化或使用
jsonify()
。 - FastAPI: 通过 Pydantic 模型自动处理。定义模型即可实现请求数据的自动校验(入参),以及响应数据的自动序列化(出参)。这极大地提高了开发效率和代码的健壮性。
6. 异步支持
- Flask: 原生不支持异步。需要在 WSGI 环境下使用协程库或在 ASGI 环境下运行。
- FastAPI: 原生支持
async
/await
。可以轻松编写异步视图函数,充分利用异步 I/O 的优势。
7. 学习曲线
- Flask: API 简单,核心概念少,非常快速地可以跑起来第一个应用。但深入学习并构建大型应用需要了解各种扩展。
- FastAPI: 入门可能需要理解类型提示、Pydantic、ASGI 等概念,初期学习曲线可能稍陡峭于 Flask 最基础的部分。但一旦掌握,后续开发会非常流畅,并且很多现代 Web 开发的最佳实践都体现在框架设计中。
8. 社区与生态系统
- Flask: 历史悠久,社区庞大,生态系统成熟稳定,几乎所有常见需求都有对应的扩展。遇到问题容易找到解决方案。
- FastAPI: 相对较新,社区仍在快速发展中,生态系统也在不断完善。核心库质量很高,但一些特定领域的扩展可能不如 Flask 成熟。但由于其基于 Starlette 和 Pydantic,也可以利用这两个库自身的生态。
9. 文档
- Flask: 官方文档质量很高,清晰详细。
- FastAPI: 官方文档被广泛赞誉,非常详细、清晰,且包含大量示例,非常适合初学者和进阶用户。加上自动生成的交互式 API 文档,开发体验极佳。
10. 适用场景
- Flask:
- 简单网站、博客。
- 简单的 API 服务。
- 原型开发。
- 需要高度定制化,不希望被框架约束的场景。
- 遗留系统维护或二次开发(如果原系统使用 Flask)。
- 团队对 Flask 及其生态非常熟悉。
- FastAPI:
- 高性能 API 服务。
- 微服务。
- 需要自动生成 API 文档的项目。
- 对数据校验和序列化有强需求的项目。
- 需要利用异步 I/O 提升并发性能的应用。
- 新项目,希望采用现代的开发实践。
- 机器学习模型的服务化。
第四部分:如何选择?给初学者的建议
对于初学者来说,选择哪个框架可能令人困惑。实际上,没有绝对的好坏,只有更适合特定场景和个人偏好的框架。
如果你是完全的新手,或者更倾向于“从零开始搭建”,喜欢自己掌控一切:
- 可以从 Flask 开始。 它的核心简单,更容易理解 Web 开发的基本原理(请求、响应、路由)。它的“微”特性迫使你去了解如何集成不同的组件(模板、数据库、表单等),这有助于构建更全面的 Web 开发知识体系。社区资源丰富,遇到问题容易找到答案。
如果你希望快速构建一个现代、高性能的 API,看重开发效率和自动化功能,或者对异步编程感兴趣:
- 可以从 FastAPI 开始。 尽管初期可能需要理解一些新概念(类型提示、Pydantic),但一旦掌握,其提供的自动化能力(数据校验、文档)会极大地提升开发效率和代码质量。特别是在构建 API 和微服务方面,FastAPI 提供了许多开箱即用的便利。其高性能和异步支持也是未来 Web 开发的重要趋势。
更实际的建议:
- 尝试都学一点: 两个框架都有其价值。花几天时间分别跟着官方教程过一遍,实现几个简单的功能(例如,一个带用户列表和用户详情页面的应用,或者一个简单的图书 API),亲身体验它们的开发流程和感受。
- 考虑项目需求: 你是想构建一个传统的网站还是高性能的 API?是否有强烈的异步需求?是否需要自动文档?项目规模预计多大?这些问题有助于你倾向于某个框架。
- 考虑团队经验: 如果你将加入一个已有项目,那么选择项目正在使用的框架是最明智的。如果是新项目,考虑团队成员对哪个框架更熟悉或更感兴趣。
- 不要害怕犯错: 框架只是工具。Python 的基础知识、Web 开发的基本原理(HTTP、REST、数据库等)是更重要的。即使开始选择了某个框架,之后发现另一个更适合,迁移虽然有成本,但并非不可能。而且学习一个框架的经验往往也能帮助你更快地学习另一个。
总的来说,Flask 是一个久经考验、灵活强大的经典微框架,适合需要高度控制或构建传统 Web 应用的场景。FastAPI 是一个现代、高性能、功能丰富的框架,特别适合构建高性能 API 服务,提供了出色的开发体验和自动化功能。两者都在不断发展,共同构成了 Python Web 开发生态的重要组成部分。
总结
本文详细介绍了 Python Web 开发中的两个重要框架:Flask 和 FastAPI。我们了解了 Flask 作为微框架的哲学、核心功能和如何使用,以及它的优劣。随后,我们深入探讨了 FastAPI 的现代设计、基于 ASGI/Pydantic/类型提示的特性和优势,以及如何快速入门。最后,我们从多个维度对比了这两个框架,并为初学者提供了选择建议。
无论你最终选择 Flask 还是 FastAPI,掌握 Web 开发的基础知识、Python 编程技能以及良好的代码实践都是至关重要的。选择一个你觉得用起来最顺手、最能激发你学习兴趣的框架,然后深入学习它,通过实践项目来巩固知识。
Web 开发的世界充满活力,不断有新的工具和技术涌现。保持好奇心,持续学习,你就能在这个领域不断进步。祝你在 Python Web 开发的旅程中一切顺利!