替代方案、灵感与比较¶
是什么启发了 FastAPI,它与替代方案相比如何,以及它从中学到了什么。
简介¶
如果没有前人的工作,FastAPI 就不会存在。
在它之前已经有许多工具被创造出来,这些工具启发了它的诞生。
我多年来一直避免创建一个新的框架。起初,我尝试使用许多不同的框架、插件和工具来解决 FastAPI 所涵盖的所有功能。
但在某个时候,除了创建一个能够提供所有这些功能的东西,取各家之长,并以尽可能最好的方式结合它们,同时利用以前甚至不可用的语言特性(Python 3.6+ 类型提示)之外,别无选择。
过往工具¶
Django¶
它是最流行的 Python 框架,且广受信任。它被用于构建像 Instagram 这样的系统。
它与关系型数据库(如 MySQL 或 PostgreSQL)耦合得相对紧密,因此,将 NoSQL 数据库(如 Couchbase、MongoDB、Cassandra 等)作为主要存储引擎并不容易。
它创建的初衷是在后端生成 HTML,而不是为了创建供现代前端(如 React、Vue.js 和 Angular)或与它通信的其他系统(如 IoT 设备)使用的 API。
Django REST Framework¶
Django REST framework 被创建为一个灵活的工具包,旨在基于 Django 构建 Web API,以改进其 API 功能。
它被许多公司使用,包括 Mozilla、Red Hat 和 Eventbrite。
它是自动 API 文档最早的例子之一,这正是启发“寻找” FastAPI 的首要想法之一。
注意
Django REST Framework 由 Tom Christie 创建。他也是 Starlette 和 Uvicorn 的作者,而 FastAPI 正是基于这两个项目。
启发 FastAPI 之处:
拥有自动化的 API 文档 Web 用户界面。
Flask¶
Flask 是一个“微框架”,它不包含数据库集成,也不包含 Django 默认提供的许多功能。
这种简洁性和灵活性使得使用 NoSQL 数据库作为主要数据存储系统等操作变得可行。
因为它非常简单,学习起来相对直观,尽管文档在某些地方显得有点技术化。
它也常用于其他不一定需要数据库、用户管理或 Django 预置功能的应用。尽管其中许多功能可以通过插件添加。
这种解耦的部件,以及作为可以扩展以满足特定需求的“微框架”,是我想要保留的一个关键特性。
鉴于 Flask 的简洁性,它似乎是构建 API 的好选择。接下来要找的是 Flask 的“Django REST Framework”。
启发 FastAPI 之处:
保持微框架特性。使得混合和匹配所需的工具和部件变得简单。
拥有简单易用的路由系统。
Requests¶
FastAPI 实际上并不是 Requests 的替代品。它们的范围截然不同。
在 FastAPI 应用程序内部使用 Requests 是很常见的。
但尽管如此,FastAPI 还是从 Requests 中汲取了不少灵感。
Requests 是一个用于(作为客户端)与 API 交互的库,而 FastAPI 是一个(作为服务器)构建 API 的库。
它们在某种程度上处于对立端,相互补充。
Requests 设计非常简洁直观,易于使用,并带有合理的默认设置。但同时,它又非常强大且可定制。
这就是为什么,正如其官方网站所言:
Requests 是有史以来下载量最大的 Python 包之一。
它的使用方式非常简单。例如,要执行一个 GET 请求,你会写:
response = requests.get("http://example.com/some/url")
FastAPI 的对应 API 路径操作看起来像这样:
@app.get("/some/url")
def read_url():
return {"message": "Hello World"}
看看 requests.get(...) 和 @app.get(...) 之间的相似之处。
启发 FastAPI 之处:
- 拥有简单直观的 API。
- 直接使用 HTTP 方法名称(操作),以一种直接且直观的方式。
- 拥有合理的默认值,但同时具备强大的自定义能力。
Swagger / OpenAPI¶
我从 Django REST Framework 中最想要的功能是自动 API 文档。
后来我发现,存在一种使用 JSON(或 JSON 的扩展 YAML)记录 API 的标准,称为 Swagger。
而且已经有一个为 Swagger API 创建的 Web 用户界面。因此,能够为 API 生成 Swagger 文档,就可以自动使用这个 Web 用户界面。
后来,Swagger 被捐赠给了 Linux 基金会,并更名为 OpenAPI。
这就是为什么谈论 2.0 版本时常说“Swagger”,而 3.0+ 版本则说“OpenAPI”。
启发 FastAPI 之处:
采用并使用开放标准进行 API 规范,而不是自定义模式。
并集成基于标准的 UI 工具:
之所以选择这两个,是因为它们非常流行且稳定,但快速搜索一下,你就能找到几十个 OpenAPI 的替代用户界面(你可以将它们与 FastAPI 一起使用)。
Flask REST 框架¶
有几个 Flask REST 框架,但在投入时间和精力进行调查后,我发现许多已经停止维护或被弃用,并且存在一些使其不适合使用的问题。
Marshmallow¶
API 系统所需的主要功能之一是数据“序列化”,即将代码(Python)中的数据转换为可以通过网络传输的内容。例如,将包含数据库数据的对象转换为 JSON 对象。将 datetime 对象转换为字符串等。
API 所需的另一个重要功能是数据验证,确保数据在给定某些参数的情况下是有效的。例如,某个字段必须是 int,而不是随机字符串。这对于传入的数据尤其有用。
如果没有数据验证系统,你就必须在代码中手动进行所有检查。
这些功能正是 Marshmallow 旨在提供的。它是一个伟大的库,我以前经常使用它。
但它是在 Python 类型提示存在之前创建的。因此,要定义每个 模式(schema),你需要使用 Marshmallow 提供的特定工具和类。
启发 FastAPI 之处:
使用代码定义“模式”,从而自动提供数据类型和验证。
Webargs¶
API 所需的另一个重要功能是解析传入请求中的数据。
Webargs 是一个旨在在多个框架(包括 Flask)之上提供该功能的工具。
它在底层使用 Marshmallow 进行数据验证。而且它是由同一批开发者创建的。
它是一个很棒的工具,在拥有 FastAPI 之前,我也经常使用它。
注意
Webargs 由 Marshmallow 的开发者创建。
启发 FastAPI 之处:
对传入的请求数据进行自动验证。
APISpec¶
Marshmallow 和 Webargs 作为插件提供验证、解析和序列化。
但缺少文档。于是 APISpec 被创建了。
它是许多框架的插件(也有一个用于 Starlette 的插件)。
它的工作方式是在处理路由的每个函数的文档字符串(docstring)中使用 YAML 格式编写模式定义。
然后它会生成 OpenAPI 模式。
这就是它在 Flask、Starlette、Responder 等框架中的工作方式。
但这样一来,我们又回到了在 Python 字符串中(一个大的 YAML)拥有微语法的问题。
编辑器对此几乎无能为力。而且如果我们修改参数或 Marshmallow 模式,却忘记同时修改 YAML 文档字符串,生成的模式就会过时。
注意
APISpec 由 Marshmallow 的开发者创建。
启发 FastAPI 之处:
支持 API 的开放标准:OpenAPI。
Flask-apispec¶
它是一个 Flask 插件,将 Webargs、Marshmallow 和 APISpec 结合在一起。
它使用来自 Webargs 和 Marshmallow 的信息,通过 APISpec 自动生成 OpenAPI 模式。
这是一个伟大的工具,被严重低估了。它本应比现有的许多 Flask 插件更受欢迎。这可能是因为它过于简洁和抽象的文档造成的。
这解决了需要在 Python 文档字符串中编写 YAML(另一种语法)的问题。
在构建 FastAPI 之前,这种 Flask、Flask-apispec 与 Marshmallow 和 Webargs 的结合是我最喜欢的后端技术栈。
使用它导致了几个 Flask 全栈生成器的创建。这些是我(以及几个外部团队)目前一直在使用的主要栈:
- https://github.com/tiangolo/full-stack
- https://github.com/tiangolo/full-stack-flask-couchbase
- https://github.com/tiangolo/full-stack-flask-couchdb
而这些相同的全栈生成器正是 FastAPI 项目生成器的基础。
注意
Flask-apispec 由 Marshmallow 的开发者创建。
启发 FastAPI 之处:
从定义序列化和验证的同一代码中自动生成 OpenAPI 模式。
NestJS (以及 Angular)¶
这甚至不是 Python,NestJS 是一个受 Angular 启发的 JavaScript (TypeScript) NodeJS 框架。
它实现了一些类似于 Flask-apispec 所能做的事情。
它有一个集成的依赖注入系统,灵感来自 Angular 2。它需要预先注册“可注入对象”(正如我知道的所有其他依赖注入系统一样),因此,它增加了代码的冗长性和重复性。
由于参数是用 TypeScript 类型(类似于 Python 类型提示)描述的,编辑器的支持相当不错。
但是,由于 TypeScript 数据在编译为 JavaScript 后不会保留,它无法同时依赖类型来定义验证、序列化和文档。由于这一点和一些设计决策,为了获得验证、序列化和自动模式生成,需要在许多地方添加装饰器。因此,它变得非常冗长。
它不能很好地处理嵌套模型。所以,如果请求中的 JSON 主体是一个包含内部字段的 JSON 对象,而这些内部字段又是嵌套的 JSON 对象,它就无法被正确记录和验证。
启发 FastAPI 之处:
使用 Python 类型以获得出色的编辑器支持。
拥有强大的依赖注入系统。寻找最小化代码重复的方法。
Sanic¶
它是第一批基于 asyncio 的极速 Python 框架之一。它的制作初衷与 Flask 非常相似。
技术细节
它使用了 uvloop 而不是默认的 Python asyncio 循环。这就是它如此之快的原因。
它显然启发了 Uvicorn 和 Starlette,目前在公开基准测试中,它们比 Sanic 更快。
启发 FastAPI 之处:
找到一种实现极致性能的方法。
这就是为什么 FastAPI 基于 Starlette,因为它是可用的最快的框架(经过第三方基准测试)。
Falcon¶
Falcon 是另一个高性能 Python 框架,它设计得非常精简,作为 Hug 等其他框架的基础。
它设计为具有接收两个参数的函数,一个是“request”,一个是“response”。然后你从请求中“读取”部分内容,并将部分内容“写入”响应。由于这种设计,无法使用标准 Python 类型提示作为函数参数来声明请求参数和主体。
因此,数据验证、序列化和文档必须在代码中手动完成,而不是自动完成。或者它们必须作为 Falcon 之上的框架来实现,如 Hug。受 Falcon 设计启发的其他框架也有同样的区别,即以一个请求对象和一个响应对象作为参数。
启发 FastAPI 之处:
寻找获得卓越性能的方法。
与 Hug 一起(因为 Hug 基于 Falcon),启发了 FastAPI 在函数中声明一个 response 参数。
尽管在 FastAPI 中它是可选的,主要用于设置响应头、Cookie 和替代的状态码。
Molten¶
我在构建 FastAPI 的早期阶段发现了 Molten。它有着非常相似的想法:
- 基于 Python 类型提示。
- 从这些类型中进行验证和文档记录。
- 依赖注入系统。
它不使用像 Pydantic 这样的第三方数据验证、序列化和文档库,它有自己的。因此,这些数据类型定义不能轻易复用。
它需要稍微冗长一点的配置。而且因为它基于 WSGI(而不是 ASGI),它没有设计用于利用 Uvicorn、Starlette 和 Sanic 等工具提供的高性能。
依赖注入系统需要预先注册依赖项,并且依赖项是基于声明的类型来解决的。因此,不可能声明多个提供某种类型的“组件”。
路由是在一个地方声明的,使用在其他地方声明的函数(而不是使用可以直接放在处理端点的函数上方的装饰器)。这比 Flask(和 Starlette)的做法更接近 Django 的做法。它在代码中将相对紧密耦合的东西分开了。
启发 FastAPI 之处:
使用模型属性的“默认”值定义数据类型的额外验证。这改进了编辑器支持,而 Pydantic 之前没有这个功能。
这实际上启发了更新 Pydantic 的部分内容,以支持相同的验证声明风格(所有这些功能现在已经在 Pydantic 中可用)。
Hug¶
Hug 是最早实现使用 Python 类型提示声明 API 参数类型的框架之一。这是一个伟大的想法,启发了其他工具做同样的事情。
它在声明中使用自定义类型而不是标准 Python 类型,但即使如此,这仍然是一个巨大的进步。
它也是最早生成以 JSON 格式声明整个 API 的自定义模式的框架之一。
它不是基于像 OpenAPI 和 JSON Schema 这样的标准。因此,将其与其他工具(如 Swagger UI)集成并不直接。但再次强调,这是一个非常有创意的想法。
它有一个有趣且不常见的特性:使用同一个框架,既可以创建 API,也可以创建 CLI。
因为它基于同步 Python Web 框架的先前标准(WSGI),它无法处理 Websockets 和其他东西,尽管它仍然具有高性能。
注意
Hug 由 Timothy Crosley 创建,他也是 isort 的作者,这是一个用于自动排序 Python 文件中导入语句的伟大工具。
启发 FastAPI 的想法
Hug 启发了 APIStar 的部分内容,它是除了 APIStar 之外,我发现最有前途的工具之一。
Hug 帮助启发了 FastAPI 使用 Python 类型提示来声明参数,并自动生成定义 API 的模式。
Hug 启发了 FastAPI 在函数中声明 response 参数来设置响应头和 Cookie。
APIStar (<= 0.5)¶
在决定构建 FastAPI 之前,我发现了 APIStar 服务器。它几乎拥有我所寻找的一切,并且设计得很棒。
这是我见过的第一批使用 Python 类型提示来声明参数和请求的框架之一(在 NestJS 和 Molten 之前)。我几乎在同一时间发现了它和 Hug。但 APIStar 使用了 OpenAPI 标准。
它具有基于相同类型提示的自动数据验证、数据序列化和 OpenAPI 模式生成。
主体模式定义没有像 Pydantic 那样使用相同的 Python 类型提示,它更像 Marshmallow,因此,编辑器支持不会那么好,但即便如此,APIStar 仍然是当时可用的最佳选择。
它在当时拥有最好的基准测试性能(仅次于 Starlette)。
起初,它没有自动 API 文档 Web UI,但我知道我可以向它添加 Swagger UI。
它有一个依赖注入系统。它需要像上面讨论的其他工具那样预注册组件。但即便如此,它仍然是一个伟大的功能。
我从来没能在完整的项目中使用它,因为它没有安全集成,所以无法替代我基于 Flask-apispec 的全栈生成器所拥有的功能。我在项目待办事项中准备提交一个添加该功能的拉取请求。
但后来,项目的重点转移了。
它不再是一个 API Web 框架,因为作者需要专注于 Starlette。
现在 APIStar 是一组验证 OpenAPI 规范的工具,而不是 Web 框架。
注意
APIStar 由 Tom Christie 创建。同一个人也创建了:
- Django REST Framework
- Starlette(FastAPI 基于它)
- Uvicorn(由 Starlette 和 FastAPI 使用)
启发 FastAPI 之处:
存在。
用相同的 Python 类型声明多项内容(数据验证、序列化和文档),同时提供出色的编辑器支持,我一直认为这是一个天才的想法。
在长期寻找类似框架并测试了许多不同的替代方案后,APIStar 是当时可用的最佳选项。
后来 APIStar 作为服务器停止存在,Starlette 被创建,成为了此类系统更好的新基础。这就是构建 FastAPI 的最终灵感。
我认为 FastAPI 是 APIStar 的“精神继承者”,它基于所有这些过往工具的经验,改进并增强了功能、类型系统和其他部分。
FastAPI 所使用的工具¶
Pydantic¶
Pydantic 是一个基于 Python 类型提示定义数据验证、序列化和文档(使用 JSON Schema)的库。
这使得它极其直观。
它与 Marshmallow 相当。尽管在基准测试中它比 Marshmallow 更快。而且因为它基于相同的 Python 类型提示,所以编辑器支持非常出色。
FastAPI 使用它来:
处理所有的数据验证、数据序列化和自动模型文档(基于 JSON Schema)。
FastAPI 然后获取这些 JSON Schema 数据并将其放入 OpenAPI 中,除此之外还做很多其他事情。
Starlette¶
Starlette 是一个轻量级的 ASGI 框架/工具包,是构建高性能 asyncio 服务的理想选择。
它非常简单直观。它的设计旨在易于扩展,并具有模块化组件。
它拥有:
- 令人印象深刻的性能。
- WebSocket 支持。
- 进程内后台任务。
- 启动和关闭事件。
- 基于 HTTPX 构建的测试客户端。
- CORS、GZip、静态文件、流式响应。
- Session 和 Cookie 支持。
- 100% 测试覆盖率。
- 100% 类型注解的代码库。
- 极少的硬依赖。
Starlette 是目前测试出的最快的 Python 框架。仅次于 Uvicorn,但 Uvicorn 不是框架,而是服务器。
Starlette 提供了所有的基础 Web 微框架功能。
但它不提供自动数据验证、序列化或文档。
这是 FastAPI 在其之上添加的主要内容,全部基于 Python 类型提示(使用 Pydantic)。再加上依赖注入系统、安全实用程序、OpenAPI 模式生成等。
技术细节
ASGI 是 Django 核心团队成员正在开发的一项新“标准”。它还不是“Python 标准”(PEP),尽管他们正在朝着这个方向努力。
尽管如此,它已经被许多工具用作“标准”。这极大地提高了互操作性,你可以将 Uvicorn 换成任何其他 ASGI 服务器(如 Daphne 或 Hypercorn),或者可以添加与 ASGI 兼容的工具,如 python-socketio。
FastAPI 使用它来:
处理所有的核心 Web 部分。并在其之上添加功能。
FastAPI 类本身直接继承自 Starlette 类。
因此,任何你可以在 Starlette 中做的事情,你都可以直接在 FastAPI 中做,因为从本质上讲,它就是打了强心针的 Starlette。
Uvicorn¶
Uvicorn 是一个闪电般快速的 ASGI 服务器,基于 uvloop 和 httptools 构建。
它不是 Web 框架,而是服务器。例如,它不提供路径路由工具。这是像 Starlette(或 FastAPI)这样的框架会在其之上提供的功能。
它是 Starlette 和 FastAPI 的推荐服务器。
基准测试与速度¶
要了解、比较并查看 Uvicorn、Starlette 和 FastAPI 之间的区别,请查看关于 基准测试 的部分。