FastAPI Route Requests
Overview
Routing is a core component of web applications, defining the mapping relationship between URL paths and handler functions. FastAPI provides a powerful and flexible routing system, supporting various HTTP methods, path parameters, query parameters, and more. This chapter will详细介绍 FastAPI's routing mechanism and request processing.
🛣️ Basic Routing Concepts
HTTP Method Support
FastAPI supports all standard HTTP methods:
from fastapi import FastAPI
app = FastAPI()
# GET request - Get resource
@app.get("/items")
async def get_items():
return {"message": "Get all items"}
# POST request - Create resource
@app.post("/items")
async def create_item():
return {"message": "Create new item"}
# PUT request - Update resource (complete update)
@app.put("/items/{item_id}")
async def update_item(item_id: int):
return {"message": f"Update item {item_id}"}
# PATCH request - Partial update resource
@app.patch("/items/{item_id}")
async def patch_item(item_id: int):
return {"message": f"Partially update item {item_id}"}
# DELETE request - Delete resource
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"message": f"Delete item {item_id}"}
# HEAD request - Get response headers (no response body)
@app.head("/items/{item_id}")
async def head_item(item_id: int):
return {"message": "HEAD request"}
# OPTIONS request - Get supported methods
@app.options("/items")
async def options_items():
return {"allowed_methods": ["GET", "POST", "PUT", "DELETE"]}Route Decorator Details
from fastapi import FastAPI, status
from typing import List, Dict, Any
app = FastAPI()
# Basic route
@app.get("/")
async def root():
return {"message": "Hello FastAPI"}
# Route with path
@app.get("/users")
async def get_users():
return {"users": []}
# Nested path
@app.get("/api/v1/users")
async def get_api_users():
return {"api_version": "v1", "users": []}
# Specify response status code
@app.post("/users", status_code=status.HTTP_201_CREATED)
async def create_user():
return {"message": "User created successfully"}
# Specify response model and status code
@app.get("/users/{user_id}", status_code=200)
async def get_user(user_id: int):
return {"user_id": user_id, "name": "John Doe"}
# Multiple decorators (same function handles multiple routes)
@app.get("/health")
@app.get("/ping")
async def health_check():
return {"status": "healthy"}🎯 Path Parameters Details
Basic Path Parameters
from fastapi import FastAPI, Path
from typing import Optional
app = FastAPI()
# Basic path parameter
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
# Multiple path parameters
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(user_id: int, item_id: int):
return {"user_id": user_id, "item_id": item_id}
# String path parameter
@app.get("/items/{item_name}")
async def read_item_by_name(item_name: str):
return {"item_name": item_name}
# Path parameter validation
@app.get("/items/{item_id}")
async def read_item_with_validation(
item_id: int = Path(
..., # Required parameter
title="Item ID",
description="Unique identifier of item to retrieve",
ge=1, # Greater than or equal to 1
le=1000 # Less than or equal to 1000
)
):
return {"item_id": item_id}Path Parameter Type Conversion
from datetime import datetime
from uuid import UUID
from enum import Enum
# Enum type parameter
class ItemType(str, Enum):
ELECTRONICS = "electronics"
CLOTHING = "clothing"
BOOKS = "books"
FOOD = "food"
@app.get("/items/{item_type}")
async def get_items_by_type(item_type: ItemType):
return {
"item_type": item_type,
"message": f"Get items of {item_type.value} type"
}
# UUID parameter
@app.get("/users/{user_id}")
async def get_user_by_uuid(user_id: UUID):
return {"user_id": str(user_id)}
# Datetime parameter
@app.get("/orders/{order_date}")
async def get_orders_by_date(order_date: datetime):
return {
"order_date": order_date.isoformat(),
"formatted_date": order_date.strftime("%Y-%m-%d %H:%M:%S")
}
# Float parameter
@app.get("/prices/{price}")
async def get_price_info(price: float):
return {
"price": price,
"tax": price * 0.1,
"total": price * 1.1
}File Path Parameters
# Capture file path
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
# Example:
# GET /files/documents/reports/2023/annual_report.pdf
# file_path = "documents/reports/2023/annual_report.pdf"
# File path with validation
@app.get("/static/{file_path:path}")
async def serve_static_file(
file_path: str = Path(
...,
title="File path",
description="Relative path to static file",
regex=r"^[a-zA-Z0-9._/\-]+$" # Only allow specific characters
)
):
# Security check
if ".." in file_path:
raise HTTPException(status_code=400, detail="Invalid file path")
return {"file_path": file_path, "message": "File access successful"}🔍 Query Parameter Processing
Basic Query Parameters
from fastapi import FastAPI, Query
from typing import Optional, List
app = FastAPI()
# Basic query parameters
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# Optional query parameters
@app.get("/items/")
async def read_items_with_query(
skip: int = 0,
limit: int = 10,
q: Optional[str] = None
):
items = {"skip": skip, "limit": limit}
if q:
items["q"] = q
return items
# Query parameter validation
@app.get("/items/")
async def read_items_validated(
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(10, ge=1, le=100, description="Number of records to return"),
q: Optional[str] = Query(
None,
min_length=3,
max_length=50,
regex="^[a-zA-Z0-9\s]+$",
description="Search query string"
)
):
return {"skip": skip, "limit": limit, "q": q}Advanced Query Parameters
from typing import List, Set
from datetime import datetime, date
# List query parameters
@app.get("/items/")
async def read_items_with_lists(
tags: List[str] = Query([], description="Tag list"),
categories: Set[str] = Query(set(), description="Category set")
):
return {
"tags": tags,
"categories": list(categories),
"unique_categories": len(categories)
}
# Example call:
# GET /items/?tags=electronics&tags=gadget&categories=tech&categories=gadget
# tags = ["electronics", "gadget"]
# categories = {"tech", "gadget"}
# Boolean query parameters
@app.get("/items/")
async def filter_items(
is_available: bool = Query(True, description="Whether available"),
is_featured: Optional[bool] = Query(None, description="Whether featured"),
on_sale: bool = False
):
return {
"is_available": is_available,
"is_featured": is_featured,
"on_sale": on_sale
}
# Date query parameters
@app.get("/orders/")
async def get_orders(
start_date: Optional[date] = Query(None, description="Start date"),
end_date: Optional[date] = Query(None, description="End date"),
created_after: Optional[datetime] = Query(None, description="Created after time")
):
return {
"start_date": start_date,
"end_date": end_date,
"created_after": created_after
}Query Parameter Aliases and Deprecation
# Parameter aliases
@app.get("/items/")
async def read_items_with_alias(
item_query: Optional[str] = Query(
None,
alias="item-query", # Use item-query in URL
title="Item query",
description="Search query string for items",
deprecated=False
)
):
return {"item_query": item_query}
# Deprecated parameters
@app.get("/items/")
async def read_items_deprecated(
q: Optional[str] = Query(None, description="Search query"),
old_query: Optional[str] = Query(
None,
deprecated=True, # Mark as deprecated
description="Old query parameter, please use q parameter"
)
):
# Compatibility handling
search_query = q or old_query
return {"search_query": search_query}🎭 Route Organization and Management
Using APIRouter
from fastapi import APIRouter, FastAPI
# Create routers
items_router = APIRouter(
prefix="/items",
tags=["items"],
responses={404: {"description": "Item not found"}}
)
users_router = APIRouter(
prefix="/users",
tags=["users"],
responses={404: {"description": "User not found"}}
)
# Item-related routes
@items_router.get("/")
async def get_items():
return {"items": []}
@items_router.get("/{item_id}")
async def get_item(item_id: int):
return {"item_id": item_id}
@items_router.post("/")
async def create_item():
return {"message": "Item created successfully"}
# User-related routes
@users_router.get("/")
async def get_users():
return {"users": []}
@users_router.get("/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
# Main application
app = FastAPI()
# Include routers
app.include_router(items_router, prefix="/api/v1")
app.include_router(users_router, prefix="/api/v1")
# Result routes:
# GET /api/v1/items/
# GET /api/v1/items/{item_id}
# POST /api/v1/items/
# GET /api/v1/users/
# GET /api/v1/users/{user_id}Router Nesting
# Create nested routers
api_v1_router = APIRouter(prefix="/api/v1")
api_v2_router = APIRouter(prefix="/api/v2")
# v1 item routes
v1_items_router = APIRouter(prefix="/items", tags=["v1-items"])
@v1_items_router.get("/")
async def get_items_v1():
return {"version": "v1", "items": []}
# v2 item routes
v2_items_router = APIRouter(prefix="/items", tags=["v2-items"])
@v2_items_router.get("/")
async def get_items_v2():
return {"version": "v2", "items": [], "new_features": True}
# Assemble routes
api_v1_router.include_router(v1_items_router)
api_v2_router.include_router(v2_items_router)
app = FastAPI()
app.include_router(api_v1_router)
app.include_router(api_v2_router)
# Result routes:
# GET /api/v1/items/ -> v1 version
# GET /api/v2/items/ -> v2 version🔧 Route Configuration and Metadata
Route Metadata
from fastapi import FastAPI, APIRouter
from typing import Dict, Any
app = FastAPI()
# Detailed route configuration
@app.get(
"/items/{item_id}",
summary="Get item details",
description="Get detailed information by item ID, including name, price, description, etc.",
response_description="Item detail information",
tags=["Item Management"],
operation_id="get_item_by_id", # OpenAPI operation ID
deprecated=False,
include_in_schema=True # Whether to include in OpenAPI schema
)
async def get_item(item_id: int):
"""
Get item details:
- **item_id**: Unique identifier of item
Returns detailed item information, including:
- Item name
- Price information
- Description content
- Available status
"""
return {"item_id": item_id, "name": "Sample item"}
# Router configuration
admin_router = APIRouter(
prefix="/admin",
tags=["Administrator"],
dependencies=[], # Dependency list
responses={
401: {"description": "Unauthorized"},
403: {"description": "Forbidden"},
500: {"description": "Server error"}
}
)
@admin_router.get(
"/stats",
summary="Get statistics",
description="Get system statistics (requires admin permission)"
)
async def get_admin_stats():
return {"users": 100, "items": 500, "orders": 1200}
app.include_router(admin_router)Conditional Routes
import os
from fastapi import FastAPI
app = FastAPI()
# Add routes based on environment
DEBUG = os.getenv("DEBUG", "false").lower() == "true"
@app.get("/")
async def read_root():
return {"message": "Production environment"}
# Only add debug routes in debug mode
if DEBUG:
@app.get("/debug")
async def debug_info():
return {"debug": True, "environment": "development"}
@app.get("/debug/config")
async def debug_config():
return {"config": dict(os.environ)}
# Feature flag routes
FEATURE_FLAGS = {
"new_ui": os.getenv("ENABLE_NEW_UI", "false").lower() == "true",
"beta_features": os.getenv("ENABLE_BETA", "false").lower() == "true"
}
if FEATURE_FLAGS["new_ui"]:
@app.get("/ui/new")
async def new_ui():
return {"ui": "new_version"}
if FEATURE_FLAGS["beta_features"]:
@app.get("/beta")
async def beta_features():
return {"features": ["feature_a", "feature_b"]}🎪 Advanced Routing Features
Route Priority and Matching
from fastapi import FastAPI
app = FastAPI()
# Route order is important! More specific routes should come first
# 1. Most specific route
@app.get("/items/latest")
async def get_latest_items():
return {"items": "latest"}
# 2. Specific value route
@app.get("/items/count")
async def get_items_count():
return {"count": 42}
# 3. Path parameter route (put last)
@app.get("/items/{item_id}")
async def get_item(item_id: int):
return {"item_id": item_id}
# Bad example (don't do this):
# If /items/{item_id} comes first,
# /items/latest will be matched as item_id="latest"Route Conflict Resolution
from fastapi import FastAPI, HTTPException, Path
from typing import Union
app = FastAPI()
# Handle conflicts using union types
@app.get("/items/{item_identifier}")
async def get_item_flexible(item_identifier: str):
# Try parsing as integer ID
if item_identifier.isdigit():
item_id = int(item_identifier)
return {"type": "id", "value": item_id}
# Handle special strings
if item_identifier in ["latest", "popular", "featured"]:
return {"type": "special", "value": item_identifier}
# Handle regular strings (possibly names or codes)
return {"type": "name", "value": item_identifier}
# Or use stricter validation
@app.get("/items/by-id/{item_id}")
async def get_item_by_id(item_id: int = Path(..., ge=1)):
return {"item_id": item_id}
@app.get("/items/by-name/{item_name}")
async def get_item_by_name(
item_name: str = Path(..., regex=r"^[a-zA-Z0-9_-]+$")
):
return {"item_name": item_name}Dynamic Route Generation
from fastapi import FastAPI, APIRouter
def create_crud_router(resource_name: str, resource_data: dict):
"""Dynamically create CRUD routes"""
router = APIRouter(prefix=f"/{resource_name}", tags=[resource_name])
@router.get("/")
async def list_resources():
return {f"{resource_name}": list(resource_data.values())}
@router.get("/{resource_id}")
async def get_resource(resource_id: int):
if resource_id in resource_data:
return resource_data[resource_id]
raise HTTPException(status_code=404, detail=f"{resource_name} not found")
@router.post("/")
async def create_resource(data: dict):
new_id = max(resource_data.keys()) + 1 if resource_data else 1
resource_data[new_id] = data
return {f"{resource_name}_id": new_id, **data}
return router
# Create multiple resource routes
app = FastAPI()
# User resources
users_data = {1: {"name": "Alice"}, 2: {"name": "Bob"}}
users_router = create_crud_router("users", users_data)
# Product resources
products_data = {1: {"name": "Laptop"}, 2: {"name": "Mouse"}}
products_router = create_crud_router("products", products_data)
# Register routes
app.include_router(users_router, prefix="/api")
app.include_router(products_router, prefix="/api")📊 Route Performance Optimization
Async vs Sync Routes
import asyncio
import time
from fastapi import FastAPI
app = FastAPI()
# Sync route (blocking)
@app.get("/sync")
def sync_endpoint():
# Simulate CPU-intensive operation
time.sleep(1)
return {"message": "Sync processing complete"}
# Async route (non-blocking)
@app.get("/async")
async def async_endpoint():
# Simulate I/O operation
await asyncio.sleep(1)
return {"message": "Async processing complete"}
# Async database operation example
@app.get("/async-db")
async def async_db_operation():
# Simulate async database query
await asyncio.sleep(0.1) # Simulate network delay
return {"data": "Data from async database"}
# Sync database operation example (not recommended)
@app.get("/sync-db")
def sync_db_operation():
# Simulate sync database query
time.sleep(0.1) # Block other requests
return {"data": "Data from sync database"}Route Caching and Optimization
from functools import lru_cache
import asyncio
# Use caching to optimize compute-intensive operations
@lru_cache(maxsize=128)
def expensive_computation(n: int) -> int:
"""Compute Fibonacci sequence (cache results)"""
if n <= 1:
return n
return expensive_computation(n-1) + expensive_computation(n-2)
@app.get("/fibonacci/{n}")
async def get_fibonacci(n: int):
if n > 40: # Prevent too large values
raise HTTPException(status_code=400, detail="Value too large")
result = expensive_computation(n)
return {"n": n, "fibonacci": result}
# Async caching example
_cache = {}
async def async_expensive_operation(key: str):
"""Simulate async expensive operation"""
if key in _cache:
return _cache[key]
# Simulate time-consuming operation
await asyncio.sleep(0.5)
result = f"Result: {key.upper()}"
_cache[key] = result
return result
@app.get("/cached/{key}")
async def get_cached_result(key: str):
result = await async_expensive_operation(key)
return {"key": key, "result": result, "cached": key in _cache}Summary
This chapter detailed FastAPI's routing system, including:
- ✅ HTTP Method Support: GET, POST, PUT, DELETE, etc.
- ✅ Path Parameters: Type conversion, validation, file paths
- ✅ Query Parameters: Basic usage, validation, list parameters
- ✅ Route Organization: APIRouter, nested routes, modularization
- ✅ Route Configuration: Metadata, conditional routes, dynamic generation
- ✅ Performance Optimization: Async processing, caching strategies
FastAPI's routing system is very flexible and powerful, supporting complex URL patterns and parameter validation. Reasonable use of these features can build both high-performance and maintainable APIs.
Route Design Recommendations
- Follow RESTful design principles
- Use clear URL structure
- Reasonably use path parameters and query parameters
- Add appropriate validation and documentation
- Consider route execution order
- Prioritize async processing for I/O operations
In the next chapter, we will learn advanced usage of path parameters and query parameters.