FastAPI 源码阅读 (一) ASGI应用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FastAPI 源码阅读 (一) ASGI应用相关的知识,希望对你有一定的参考价值。

参考技术A

本章开启 FastAPI 的源码阅读,FastAPI是当下python web中一颗新星,是一个划时代的框架。从诞生便是以快速和简洁为核心理念。
它继承于 Starlette ,是在其基础上的完善与扩展。详细内容可以翻看我之前的源码阅读。

openapi() 与 setup() 是在初始化阶段,对 OpenAPI 文档进行初始化的函数。
而 add_api_route() 一直到 trace() ,是关于路由的函数,它们都是直接对 router 的方法传参引用。所以这些放在解读 routing.py 时一并进行。

除了 Starlette 原生的参数,大量参数都是和API文档相关。
而路由从 Starlette 的 Router 换成了新式的 APIRouter

这两个概念查询了大量文档才搞明白。他们主要是关于文档与反向代理的参数。当使用了nginx时等反向代理时,从Uvicorn直接访问,和从Nginx代理访问,路径可能出现不一致。比如Nginx中的Fastapi根目录是 127.0.0.1/api/ ,而Uvicorn角度看是 127.0.0.1:8000/ 。对于API接口来说,其实这个是没有影响的,因为服务器会自动帮我们解决这个问题。但对于API文档来说,就会出现问题。

因为当我们打开/docs时,网页会寻找 openapi.json 。他的是写在html内部的,而不是变化的。这会导致什么问题?

例如当我们从Uvicorn访问 127.0.0.1:8000/docs 时,他会寻找 /openapi.json 即去访问 127.0.0.1:8000/openapi.json (了解前端的应该知道)

但是假如我们这时,从Nginx外来访问文档,假设我们这样设置Nginx:

我们需要访问 127.0.0.1/api/docs ,才能从代理外部访问。而打开docs时,我们会寻找 openapi.json

所以这时,它应该在 127.0.0.1/api/openapi 这个位置存在。
但我们的浏览器不知道这些,他会按照 /openapi.json ,会去寻 127.0.0.1/openapi.json 这个位置。所以他不可能找到 openapi.json ,自然会启动失败。

这其实是openapi文档前端的问题。

root_path ,是用来解决这个问题的。既然 /openapi.json 找不到,那我自己改成 /api/openapi.json 不就成了么。
root_path 即是这个 /api ,这个是在定义时手动设置的参数。为了告诉FastAPI,它处在整个主机中的哪个位置。即告知 所在根目录 。这样,FastAPI就有了"自知之明",乖乖把这个 前缀 加上。来找到正确的 openapi.json

加上了 root_path openapi.json 的位置变成了 /api/openapi.json 。当你想重新用Uvicorn提供的地址从代理内访问时,他会去寻找哪?没错 127.0.0.1:8000/api/openapi.json ,但我们从代理内部访问,并不需要这个 前缀 ,但它还是 “善意” 的帮我们加上了,所以这时候内部的访问失灵了。

虽然我们不大可能需要从两个位置访问这个完全一样的api文档。但这点一定要注意。

我在翻官方文档时,看到他们把 root_path 吹得天花乱坠,甚至弃用了 openapi_prefix 参数。但最后是把我弄得晕头转向。

这样要提到 servers 这个参数,官方首先给了这么段示例,稍作修改。

当我们打开API文档时

我们可以切换这个 Servers 时,底下测试接口的发送链接也会变成相应的。

但是记住,切换这个server,下面的接口不会发送变化,只是发送的host会改变。
这代表,虽然可以通过文档,测试多个其他主机的接口。但是这些主机和自己之间,需要拥有一致的接口。这种情况通常像在 线上/开发服务器 或者 服务器集群 中可能出现。虽然不要求完全一致,但为了这样做有实际意义,最好大体上是一致的。

但是我们看到,这是在代理外打开的,如果我们想从代理内打开,需要去掉 root_path 。会发生什么?
我们将 root_path 注释掉:

如果想解决这个问题,只需要将自身手动加入到 Servers 中。

关于 root_path_in_servers ,当 root_path servers 都存在时, root_path 会自动将自己加入到 servers 中。但如果这个置为False,就不会自动加入。(默认为True)

API文档实际上以字符串方式,在FastAPI内部拼接的。实际上就是传统的 模板(Templates) ,这个相信大家都很熟悉了。优点是生成时灵活,但缺点是不容易二次开发。fastapi提供了好几种文档插件,也可以自己添加需要的。

这么长一大串,实际上就一句话 self.router.add_api_route() ,其他剩下的那些我暂且省略的,其实基本都是这样的。就是调用router的一个功能。下面我用省略方式将它们列出。

可以看到有些在这里就做了闭包,实际上除了这里的\'add_api_route()\'他们最终都是要做闭包的。只是过程放在里router里。它们最终的指向都是 router.add_api_route() ,这是一个添加真正将 endpoint 加入到路由中的方法。
FastAPI 添加路由的方式,在starlette的传统路由列表方式上做了改进,变成了装饰器式。

其实就是通过这些方法作为装饰器,将自身作为 endpoint 传入生成 route 节点,加入到 routes 中。

FastAPI的入口没有太大的变化,借用starlette的 await self.middleware_stack(scope, receive, send) 直接进入中间件堆栈。

以上是关于FastAPI 源码阅读 (一) ASGI应用的主要内容,如果未能解决你的问题,请参考以下文章

FastAPI 1:安装FastAPI

FastAPI 源码阅读 (五) 其余主体内容

FastAPI 源码阅读 (四) Endpoint解析

搭建ASGI高性能web服务器FastAPI,python

搭建ASGI高性能web服务器FastAPI,python

python项目实战:实时博客项目 源码下载