FastAPI 的应用挂载是如何工作的?
How does FastAPI's application mounting works?
出于某些原因,我们选择了 FastAPI,以便将其用作我们多模块生产的后端层。它的一个吸引人的特性是子应用程序,它帮助我们分离不同的模块,以使其更加模块化。但是我们担心官方文档中缺少一些可能存在的缺陷。有相当多的共同事物——例如数据、服务等——我们需要通过插件、中间件和依赖注入在主模块和子模块之间共享它们。问题是:这个特性对于单独的模块是否足够好?等等:子应用程序是否从父应用程序继承中间件、插件和依赖注入?
感谢分享您的经验。
the sample code in the official docs
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
return {"message": "Hello World from sub API"}
app.mount("/subapi", subapi)
我认为文档对此非常清楚。
"Mounting" means adding a completely "independent" application.
无论如何,让我们从您的示例继续。
这就是我们为我们的子 API 的路由得到的。
[{"path":route.path} for route in subapi.routes] = [
{'path': '/openapi.json'},
{'path': '/docs'},
{'path': '/docs/oauth2-redirect'},
{'path': '/redoc'},
{'path': '/sub'}
]
这就是我们得到的应用程序路由。
[{"path":route.path} for route in app.routes] = [{'path': '/openapi.json'},
{'path': '/docs'},
{'path': '/docs/oauth2-redirect'},
{'path': '/redoc'},
{'path': '/app'},
{'path': '/subapi'}
]
这很有趣,因为我们的 subapi 没有继承 /app
,让我们继续前进,让事情变得更有趣,让我们 运行 我们的应用程序只需一个命令
uvicorn my_app_name:app
正如预期的那样,我们在 /docs
中提供了我们应用程序的文档
我们在 /subapi/docs
中也有 subapi 的文档,这里没什么意思。
那么当我们添加这个时我们应该期待什么?
subapi.mount("/app", app)
让我们再 运行 一次,但这次我们调用 subapi。
uvicorn my_app_name:subapi
我们期待看到什么?
- 默认情况下我们应该在
/docs
中有 subapi 的文档
- 应用程序的文档在
/app/docs
是的,我们是对的,但事情从这里开始变得有趣。
现在我们有了套娃这样的应用程序
当我们向 /app/subapi/sub
发送请求时(提醒我们 运行 我们的应用 uvicorn my_app_name:subapi
)
curl http://127.0.0.1:8000/app/subapi/sub
Out: {"message":"Hello World from sub API"}
看起来它工作正常,但让我们尝试更多。
那/app/subapi/app/subapi/app/subapi/app/subapi/app/subapi/app/app
呢
curl http://127.0.0.1:8000/app/subapi/app/subapi/app/subapi/app/subapi/app/subapi/app/app
Out: {"message":"Hello World from main app"}
你是不是一头雾水?别这样,让我解释一下。
当您挂载 sub-application 时,FastAPI 会使用 ASGI 规范中称为 root_path
的机制来处理已挂载的应用程序
root_path
的作用以及上述示例为何有效?
Straight-forward root_path
说,你可以从你的 root_path
到达你在 app.routes
中定义的所有路线,让我们想象一下。
现在我们的 root_path
是 /app
/app/
我们添加subapi,就成了我们的root_path
.
/app/subapi/
我们再添加app,就成了我们的root_path
/app/subapi/app
- 注意:上面的示例之所以有效,是因为我们将两个应用程序安装在一起。
你是不是不满意,你说如果我加一个中间件,会怎么样?
简单回答,不会继承。
让我用一个简单的例子来解释一下,我将为我的子 API 添加一个中间件。
from fastapi.middleware.cors import CORSMiddleware
subapi.add_middleware(CORSMiddleware)
您的应用程序的所有数据都在 __dict__
中
所以我们可以通过检查 'user_middleware' 键轻松找出差异。
subapi.__dict__['user_middleware'] = [Middleware(CORSMiddleware)]
app.__dict__['user_middleware'] = []
您添加的所有其他内容等都将独立工作,因为它们在底层是完全不同的应用程序,因此您将安全地使用挂载。
结论
- 是的,他们会独立工作。
出于某些原因,我们选择了 FastAPI,以便将其用作我们多模块生产的后端层。它的一个吸引人的特性是子应用程序,它帮助我们分离不同的模块,以使其更加模块化。但是我们担心官方文档中缺少一些可能存在的缺陷。有相当多的共同事物——例如数据、服务等——我们需要通过插件、中间件和依赖注入在主模块和子模块之间共享它们。问题是:这个特性对于单独的模块是否足够好?等等:子应用程序是否从父应用程序继承中间件、插件和依赖注入?
感谢分享您的经验。
the sample code in the official docs
from fastapi import FastAPI
app = FastAPI()
@app.get("/app")
def read_main():
return {"message": "Hello World from main app"}
subapi = FastAPI()
@subapi.get("/sub")
def read_sub():
return {"message": "Hello World from sub API"}
app.mount("/subapi", subapi)
我认为文档对此非常清楚。
"Mounting" means adding a completely "independent" application.
无论如何,让我们从您的示例继续。
这就是我们为我们的子 API 的路由得到的。
[{"path":route.path} for route in subapi.routes] = [
{'path': '/openapi.json'},
{'path': '/docs'},
{'path': '/docs/oauth2-redirect'},
{'path': '/redoc'},
{'path': '/sub'}
]
这就是我们得到的应用程序路由。
[{"path":route.path} for route in app.routes] = [{'path': '/openapi.json'},
{'path': '/docs'},
{'path': '/docs/oauth2-redirect'},
{'path': '/redoc'},
{'path': '/app'},
{'path': '/subapi'}
]
这很有趣,因为我们的 subapi 没有继承 /app
,让我们继续前进,让事情变得更有趣,让我们 运行 我们的应用程序只需一个命令
uvicorn my_app_name:app
正如预期的那样,我们在
中提供了我们应用程序的文档/docs
我们在
/subapi/docs
中也有 subapi 的文档,这里没什么意思。
那么当我们添加这个时我们应该期待什么?
subapi.mount("/app", app)
让我们再 运行 一次,但这次我们调用 subapi。
uvicorn my_app_name:subapi
我们期待看到什么?
- 默认情况下我们应该在
/docs
中有 subapi 的文档
- 应用程序的文档在
/app/docs
是的,我们是对的,但事情从这里开始变得有趣。
现在我们有了套娃这样的应用程序
当我们向 /app/subapi/sub
发送请求时(提醒我们 运行 我们的应用 uvicorn my_app_name:subapi
)
curl http://127.0.0.1:8000/app/subapi/sub
Out: {"message":"Hello World from sub API"}
看起来它工作正常,但让我们尝试更多。
那/app/subapi/app/subapi/app/subapi/app/subapi/app/subapi/app/app
curl http://127.0.0.1:8000/app/subapi/app/subapi/app/subapi/app/subapi/app/subapi/app/app
Out: {"message":"Hello World from main app"}
你是不是一头雾水?别这样,让我解释一下。
当您挂载 sub-application 时,FastAPI 会使用 ASGI 规范中称为 root_path
root_path
的作用以及上述示例为何有效?
Straight-forward root_path
说,你可以从你的 root_path
到达你在 app.routes
中定义的所有路线,让我们想象一下。
现在我们的 root_path
是 /app
/app/
我们添加subapi,就成了我们的root_path
.
/app/subapi/
我们再添加app,就成了我们的root_path
/app/subapi/app
- 注意:上面的示例之所以有效,是因为我们将两个应用程序安装在一起。
你是不是不满意,你说如果我加一个中间件,会怎么样?
简单回答,不会继承。
让我用一个简单的例子来解释一下,我将为我的子 API 添加一个中间件。
from fastapi.middleware.cors import CORSMiddleware
subapi.add_middleware(CORSMiddleware)
您的应用程序的所有数据都在 __dict__
所以我们可以通过检查 'user_middleware' 键轻松找出差异。
subapi.__dict__['user_middleware'] = [Middleware(CORSMiddleware)]
app.__dict__['user_middleware'] = []
您添加的所有其他内容等都将独立工作,因为它们在底层是完全不同的应用程序,因此您将安全地使用挂载。
结论
- 是的,他们会独立工作。