OpenHands 源码解析 · 第 14 讲
OpenHands 源码解析系列 第 14 讲:API 路由设计与中间件 基于 OpenHands 源码 · 2026-08-10 一、FastAPI 应用入口 OpenHands V1 的整个 Web 服务是一个 FastAPI 应用,入口在 openhands/app_server/app.py 。这个文件只有 86 行,却包含了应用的完整骨架: 📄 openhands/app_server/app.py (第 54-86 行) app = FastAPI( title='OpenHands', description='OpenHands: Code Less, Make More', version=get_version(), lifespan=combine_lifespans(*lifespans), routes=[Mount(path='/mcp', app=mcp_app)], ) @app.exception_handler(AuthenticationError) async def authentication_error_handler(request: Request, exc: AuthenticationError): return JSONResponse( status_code=401, content=str(exc), ) app.include_router(v1_router.router) app.include_router(health_router) if os.getenv('SERVE_FRONTEND', 'true').lower() == 'true': if os.path.isdir('./frontend/build'): app.mount( '/', SPAStaticFiles(directory='./frontend/build', html=True), name='dist' ) app.add_middleware(LocalhostCORSMiddleware) app.add_middleware(CacheControlMiddleware) app.add_middleware( RateLimitMiddleware, rate_limiter=InMemoryRateLimiter(requests=10, seconds=1), ) 关键设计点: 🔹 Lifespan 组合器 :用 combine_lifespans() 把 MCP 服务和 AppLifespanService 的生命周期管理器串起来,通过 AsyncExitStack 实现嵌套的异步上下文管理 🔹 路由注册顺序 :先注册 v1_router (所有 /api/v1/*),再注册 health_router (/alive、/health),最后挂载前端静态资源 🔹 中间件注册顺序 :CORS → Cache-Control → Rate-Limit,注意 FastAPI 中间件是 后进先出 (LIFO),所以 Rate-Limit 最先执行 🔹 MCP 挂载 :MCP 服务器通过 Mount(path='/mcp') 直接挂载到 FastAPI 根路由,独立于 v1 路由树 二、V1 路由聚合器 所有 API 路由统一挂在 /api/v1 前缀下。聚合器 v1_router.py 只做了路由组合,不写业务逻辑: 📄 openhands/app_server/v1_router.py (第 1-37 行,全文) from fastapi import APIRouter from openhands.app_server.app_conversation import app_conversation_router from openhands.app_server.config_api.config_router import router as config_router from openhands.app_server.event import event_router from openhands.app_server.event_callback impo…