示例
fastapi 0.141.1: Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence
已验证示例 — pypi fastapi 0.141.1: Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence. contract 在 python…
sha256:f0072757a2777940751ef402b9be2ab1a50015019493ad42d2c8972723843885
本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。
提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。
MIT-0
执行证据
声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。
- 证据依据
- 签名契约通过
- 验证回执
- 1
- 构建过它的签名密钥
- 1
声明的环境
python linux 24 · ubuntu · glibc 2.39 x64 python python pip
验证运行环境
| 环境 | 契约 | 阶段 | 运行日期 |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1python:3.12-alpine@sha256:d09d15e60962… |
2026-08-27 |
案例
HOW- 目标
- Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence
- 符号
-
- fastapi.APIRouter
- fastapi.FastAPI
- fastapi.Depends
- fastapi.HTTPException
- fastapi.testclient.TestClient
- 环境
- python
- 创建时间
- 2026-08-27T21:40:39Z
契约
- assert APIRouter prefix concatenates with include_router prefix so child router routes are exposed under parent and app prefixes
- assert router-level dependencies execute before endpoint-level dependencies and block unauthorized requests with HTTPException
- assert nested routers inherit parent router dependencies and execute them in top-down hierarchical order
- assert static route registered before parameterized route matches exact path instead of capturing parameter
- assert route response_model on APIRouter filters dictionary output to declared Pydantic schema fields
- assert APIRouter status_code parameter sets HTTP status code on response
- assert app.dependency_overrides intercepts dependencies declared at the APIRouter level
- assert direct endpoint function call bypasses APIRouter dependency resolution and routing decorators
文件
- NOTES.md
- PROMPT.md
- csx.json
- requirements.txt
- spec.json
- src/__init__.py
- src/app.py
- test/contract.py
源代码
# FastAPI APIRouter Modular Routing, Dependency Cascading, and Path Precedence
## Search Results
`search_known_solution` returned `NO_SAFE_MATCH` for `fastapi.APIRouter` modular routing with nested router hierarchies, router-level dependency cascading, and path evaluation precedence. Existing samples for `pkg:pypi/fastapi@0.141.1` covered general TestClient error payloads, query parameter binding, header validation, and generator dependency lifecycles, but lacked modular APIRouter composition contracts.
## What a Model Would Have Written
A model writing FastAPI APIRouter applications often assumes:
1. Static routes defined after dynamic path parameter routes (e.g. `/users/{user_id}` placed before `/users/me`) will still resolve `/users/me` specifically to the `/me` handler.
2. Router-level dependencies (`APIRouter(dependencies=[...])`) only apply to direct routes, not cascaded down to child routers attached via `parent_router.include_router(child_router)`.
3. Dependency overrides via `app.dependency_overrides` only intercept endpoint-level `Depends(...)` declarations and miss router-level `dependencies` lists.
## How the Wrong Version Fails
- Placing dynamic path parameter routes before static routes causes FastAPI to eagerly match `/users/me` against `/users/{user_id}` with `user_id="me"`, either returning unexpected data or failing type validation (e.g. 422 Unprocessable Entity if `user_id: int`).
- In nested routers, assuming parent dependencies are not inherited leads to missing authentication/authorization checks on sub-router endpoints if they aren't explicitly duplicated.
- Direct endpoint function invocation outside the ASGI pipeline bypasses APIRouter dependency verification and response schema filtering entirely.
Clean-room public code sample — generation instructions
Write a brand-new, minimal, self-contained code sample in this clean-room directory.
Do not copy, paraphrase, or reference any existing project source. Work only from this spec.
A csx.json manifest scaffold already exists. Do not recreate it from memory. Preserve its case.goal, packages and symbols; fill its empty case.contract with exact assertions and correct its environment, commands and verifierAdapter for the files you generate.
Goal: Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence
Kind: HOW
Use EXACTLY these public packages and versions:
- pkg:pypi/fastapi@0.141.1
Demonstrate these symbols/APIs:
- fastapi.APIRouter
Rules:
- One focused purpose; the smallest project that proves the goal.
- Include a contract test (test/contract.*) that runs OFFLINE and exits 0 exactly when the goal behavior works.
- Pin every dependency with a lockfile so resolution is reproducible.
- No secrets, credentials, or tokens. No real URLs (only example.com or localhost). No absolute paths.
- No personal names, emails, company names, or project identifiers of any kind.
- No binaries and no generated output (node_modules, dist, target, venv, .git, .env).
- Keep it under 200 files and 256KB packed.
{"case":{"caseId":"case:sha256:3775d81e18907b504305ec9c9b9ba9538e28585019469743385bbe397a307c74","contract":["assert APIRouter prefix concatenates with include_router prefix so child router routes are exposed under parent and app prefixes","assert router-level dependencies execute before endpoint-level dependencies and block unauthorized requests with HTTPException","assert nested routers inherit parent router dependencies and execute them in top-down hierarchical order","assert static route registered before parameterized route matches exact path instead of capturing parameter","assert route response_model on APIRouter filters dictionary output to declared Pydantic schema fields","assert APIRouter status_code parameter sets HTTP status code on response","assert app.dependency_overrides intercepts dependencies declared at the APIRouter level","assert direct endpoint function call bypasses APIRouter dependency resolution and routing decorators"],"goal":"Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence","kind":"HOW","packages":["pkg:pypi/fastapi@0.141.1","pkg:pypi/starlette@1.6.0","pkg:pypi/pydantic@2.13.4","pkg:pypi/httpx@0.28.1"],"schemaVersion":1,"symbols":["fastapi.APIRouter","fastapi.FastAPI","fastapi.Depends","fastapi.HTTPException","fastapi.testclient.TestClient"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","distro":"ubuntu","ecosystem":"pypi","executionContext":"python","language":"python","libc":"glibc","libcVersion":"2.39","os":"linux","osVersionBucket":"24","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/fastapi@0.141.1","pkg:pypi/starlette@1.6.0","pkg:pypi/pydantic@2.13.4","pkg:pypi/httpx@0.28.1"],"schemaVersion":1,"subject":"pkg:pypi/fastapi@0.141.1","symbols":["fastapi.APIRouter","fastapi.FastAPI","fastapi.Depends","fastapi.HTTPException","fastapi.testclient.TestClient"],"verifierAdapter":"python@1"}
fastapi==0.141.1
starlette==1.6.0
annotated-doc==0.0.5
pydantic==2.13.4
pydantic-core==2.46.4
typing-extensions==4.16.0
typing-inspection==0.4.4
annotated-types==0.8.0
anyio==4.14.2
idna==3.18
httpx2==2.10.0
httpcore2==2.10.0
truststore==0.10.4
httpx==0.28.1
httpcore==1.0.9
h11==0.16.0
certifi==2026.7.22
{
"schemaVersion": 1,
"goal": "Modular routing with FastAPI APIRouter prefix nesting route dependencies response models and path precedence",
"kind": "HOW",
"packages": [
"pkg:pypi/fastapi@0.141.1"
],
"symbols": [
"fastapi.APIRouter"
]
}
"""Package root for src."""
"""FastAPI application demonstrating APIRouter modular routing, dependency cascading, and path precedence."""
from typing import Any
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Header, status
from pydantic import BaseModel
app = FastAPI()
AUDIT_TRAIL: list[str] = []
# --- Router & Endpoint Dependencies ---
def verify_api_key(x_api_key: str = Header(default="")) -> str:
AUDIT_TRAIL.append(f"auth:api_key:{x_api_key}")
if x_api_key != "secret-token":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API key",
)
return x_api_key
def verify_admin_role(x_role: str = Header(default="user")) -> str:
AUDIT_TRAIL.append(f"auth:role:{x_role}")
if x_role != "admin":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin role required",
)
return x_role
# --- Pydantic Models for Response Filtering ---
class ItemCreate(BaseModel):
name: str
price: float
secret_code: str
class ItemResponse(BaseModel):
id: int
name: str
price: float
# --- Root / Base Routers ---
# 1. Parent API Router with router-level dependency
api_v1_router = APIRouter(
prefix="/api/v1",
dependencies=[Depends(verify_api_key)],
tags=["v1"],
)
# 2. Child Router for Items, nested inside api_v1_router
items_router = APIRouter(prefix="/items", tags=["items"])
# Path Precedence: Static route "/me" registered BEFORE parameterized route "/{item_id}"
@items_router.get("/me")
def get_current_user_items() -> dict[str, str]:
AUDIT_TRAIL.append("endpoint:get_current_user_items")
return {"owner": "current_user", "status": "active"}
@items_router.get("/{item_id}")
def get_item_by_id(item_id: int) -> dict[str, Any]:
AUDIT_TRAIL.append(f"endpoint:get_item_by_id:{item_id}")
return {"id": item_id, "name": f"Item-{item_id}"}
@items_router.post(
"",
response_model=ItemResponse,
status_code=status.HTTP_201_CREATED,
)
def create_item(item: ItemCreate) -> dict[str, Any]:
AUDIT_TRAIL.append(f"endpoint:create_item:{item.name}")
return {
"id": 101,
"name": item.name,
"price": item.price,
"secret_code": item.secret_code,
}
# 3. Admin Child Router with additional router-level dependency
admin_router = APIRouter(
prefix="/admin",
dependencies=[Depends(verify_admin_role)],
tags=["admin"],
)
@admin_router.get("/dashboard")
def get_admin_dashboard() -> dict[str, str]:
AUDIT_TRAIL.append("endpoint:get_admin_dashboard")
return {"status": "ok", "panel": "admin_main"}
# Mount child routers onto parent router
api_v1_router.include_router(items_router)
api_v1_router.include_router(admin_router)
# Include parent router into the main FastAPI application
app.include_router(api_v1_router)
# 4. Standalone Public Router without auth
public_router = APIRouter(prefix="/public")
@public_router.get("/health")
def health_check() -> dict[str, str]:
return {"status": "healthy"}
app.include_router(public_router)
import sys
from pathlib import Path
# Insert project root for module imports
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import fastapi
from fastapi import __version__ as fastapi_version
from fastapi.testclient import TestClient
from src.app import (
AUDIT_TRAIL,
app,
get_item_by_id,
verify_api_key,
)
client = TestClient(app)
# 1. Router prefix concatenation and sub-router routing
# Routes should be available under /api/v1/items/...
AUDIT_TRAIL.clear()
res_health = client.get("/public/health")
assert res_health.status_code == 200
assert res_health.json() == {"status": "healthy"}
# 2. Router-level dependencies block unauthorized requests
# /api/v1/... requires X-API-Key header defined at api_v1_router level
res_unauth = client.get("/api/v1/items/42")
assert res_unauth.status_code == 401
assert res_unauth.json()["detail"] == "Invalid or missing API key"
# Authorized request executes router-level dependency before route handler
AUDIT_TRAIL.clear()
res_auth = client.get("/api/v1/items/42", headers={"x-api-key": "secret-token"})
assert res_auth.status_code == 200
assert res_auth.json() == {"id": 42, "name": "Item-42"}
assert AUDIT_TRAIL == [
"auth:api_key:secret-token",
"endpoint:get_item_by_id:42",
], "Router dependency must execute before endpoint handler"
# 3. Nested router dependency cascading (parent dependency + child dependency)
# /api/v1/admin/dashboard inherits api_v1_router auth and adds admin_router auth
AUDIT_TRAIL.clear()
res_admin_no_role = client.get(
"/api/v1/admin/dashboard",
headers={"x-api-key": "secret-token", "x-role": "user"},
)
assert res_admin_no_role.status_code == 403
assert res_admin_no_role.json()["detail"] == "Admin role required"
assert AUDIT_TRAIL == [
"auth:api_key:secret-token",
"auth:role:user",
], "Parent router dependency executes first, followed by nested router dependency"
AUDIT_TRAIL.clear()
res_admin_ok = client.get(
"/api/v1/admin/dashboard",
headers={"x-api-key": "secret-token", "x-role": "admin"},
)
assert res_admin_ok.status_code == 200
assert res_admin_ok.json() == {"status": "ok", "panel": "admin_main"}
assert AUDIT_TRAIL == [
"auth:api_key:secret-token",
"auth:role:admin",
"endpoint:get_admin_dashboard",
]
# 4. Path precedence: static route before parameterized route
# /api/v1/items/me matches get_current_user_items, NOT get_item_by_id with item_id="me"
AUDIT_TRAIL.clear()
res_me = client.get("/api/v1/items/me", headers={"x-api-key": "secret-token"})
assert res_me.status_code == 200
assert res_me.json() == {"owner": "current_user", "status": "active"}
assert AUDIT_TRAIL == [
"auth:api_key:secret-token",
"endpoint:get_current_user_items",
]
# Parameterized route with invalid type triggers 422
res_invalid_id = client.get(
"/api/v1/items/invalid-number",
headers={"x-api-key": "secret-token"},
)
assert res_invalid_id.status_code == 422
# 5. Response model filtering and status_code on APIRouter endpoints
AUDIT_TRAIL.clear()
res_create = client.post(
"/api/v1/items",
headers={"x-api-key": "secret-token"},
json={"name": "Widget", "price": 19.99, "secret_code": "TOP_SECRET"},
)
assert res_create.status_code == 201
body = res_create.json()
assert body == {"id": 101, "name": "Widget", "price": 19.99}
assert "secret_code" not in body, "response_model must filter out undeclared secret fields"
# 6. Dependency overrides intercept router-level dependencies
app.dependency_overrides[verify_api_key] = lambda: "mocked-key"
AUDIT_TRAIL.clear()
res_overridden = client.get("/api/v1/items/99")
assert res_overridden.status_code == 200
assert res_overridden.json() == {"id": 99, "name": "Item-99"}
app.dependency_overrides.clear()
# 7. Direct function invocation bypasses APIRouter dependency and routing pipeline
direct_val = get_item_by_id(item_id=7)
assert direct_val == {"id": 7, "name": "Item-7"}
print(f"APIRouter contract verified successfully against fastapi {fastapi_version}")
原始种子者
匿名