如何将FastAPI应用根路由拆分到独立文件并避免路径报错
解决FastAPI根路由拆分到独立文件的路径冲突问题
问题背景
尝试将网站根路由拆分到独立的website.py文件实现解耦时,频繁触发FastAPIError: Prefix and path cannot be both empty (path operation: root)错误,即使设置前缀为''或'/'也无法解决。目标是通过app.include_router(website.router)让根路由正常指向website.py,替代main.py中当前可正常运行的根路由代码。
错误原因
在APIRouter中定义了空字符串路径'',同时使用include_router时未指定前缀(或前缀为空),导致FastAPI检测到路径组合冲突——前缀和路径同时为空是不允许的。
解决方案
1. 修正website.py中的路由定义
删除空路径的装饰器@router.get('', include_in_schema=False),仅保留@router.get("/");或者将空路径改为'/',避免和空前缀冲突。
2. 调整main.py中的路由引入
取消website模块的导入注释,取消app.include_router(website.router)的注释,无需额外指定前缀(默认前缀为空即可匹配根路径)。
修改后的代码
main.py
from datetime import datetime, timedelta, timezone from fastapi import FastAPI, Request, Depends, Form from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from asgi_csrf import asgi_csrf from routers import signup, users, website # 取消website的导入注释 templates = Jinja2Templates(directory="templates") app = FastAPI( root_path="/dev" ) app.mount("/static", StaticFiles(directory="static"), name="static") # 原根路由代码已移至website.py,此处注释或删除 # @app.get('', include_in_schema=False) # @app.get("/") # async def root(request: Request, id: str = ""): # response = templates.TemplateResponse( # request=request, name="index.html", context={"id": id} # ) # return response app.include_router(website.router) # 取消该注释 app.include_router(signup.router, prefix="/signup") app.include_router(users.router, prefix="/users")
website.py
from datetime import datetime, timedelta, timezone import uuid from typing import Annotated from fastapi import APIRouter, Request, Cookie, Header, Depends, Form from fastapi.templating import Jinja2Templates from pydantic import BaseModel router = APIRouter() templates = Jinja2Templates(directory="templates") # 仅保留一个根路径装饰器,删除空字符串路径的定义 @router.get("/") async def root(request: Request, id: str = ""): response = templates.TemplateResponse( request=request, name="index.html", context={"id": id} ) return response
如果需要保留include_in_schema=False的配置,也可以写成:
@router.get("/", include_in_schema=False) @router.get("/") async def root(request: Request, id: str = ""): # ... 原有代码
这样既保留了schema隐藏的逻辑,又避免了空路径冲突。
内容的提问来源于stack exchange,提问作者Johnny John Boy
相关产品推荐
相关产品推荐

