Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Managing Response Models, Status Codes, File Handling, and Error Processing in FastAPI

Tech May 10 4

Selecting and Filtering Output with Response Models

When an endpoint uses a Pydantic schema as its output model, you can control wich fields appear in the JSON response. The parameter response_model_exclude_unset skips fields that retain their default values. Only explicit provided data is sent back, even if the provided value happens to match the default.

from typing import Optional
from fastapi import APIRouter
from pydantic import BaseModel, EmailStr

router = APIRouter()

class UserCreate(BaseModel):
    name: str
    secret: str
    contact: EmailStr
    phone: str = "10086"
    location: str = None
    alias: Optional[str] = None

class UserView(BaseModel):
    name: str
    contact: EmailStr
    phone: str = "10086"
    location: str = None
    alias: Optional[str] = None

fake_users = {
    "a1": {"name": "a1", "secret": "abc", "contact": "a1@mail.com"},
}

@router.post(
    "/users",
    response_model=UserView,
    response_model_exclude_unset=True
)
def add_user(payload: UserCreate):
    """Only supplied fields and unchanged defaults show up when `exclude_unset` is enabled."""
    print(payload.secret)  # secret is excluded from response
    return fake_users["a1"]

For targeted control, response_model_include specifies the exact set of output fields, while response_model_exclude removes particular fields regardless of thier values.

@router.post(
    "/users/custom",
    response_model=UserView,
    response_model_include=["name", "contact", "phone"],
    response_model_exclude=["phone"]
)
def custom_output(payload: UserCreate):
    """Whitelist fields with include, then blacklist with exclude."""
    return payload

Status Codes and Route Metadata

Assign a status code to a route directly with status_code or use FastAPI’s status constants for clarity.

from fastapi import status

@router.post("/echo-status", status_code=204)
def echo_nothing():
    return None

@router.get("/code-detail", status_code=status.HTTP_202_ACCEPTED)
def detail_status():
    return {"accepted": True}

Enhance OpenAPI documentation and mark a deprecated endpoint using Path Operation Configuration arguments.

@router.patch(
    "/legacy",
    response_model=UserView,
    summary="Update user legacy",
    description="This path is replaced by a newer version.",
    response_description="Updated user data",
    deprecated=True,
    status_code=status.HTTP_200_OK
)
def legacy_update(payload: UserCreate):
    return payload.dict()

Processing Form Data

Parse submitted HTML forms by declaring Form fields in the route signature.

from fastapi import Form

@router.post("/session/login")
def handle_login(account: str = Form(...), credential: str = Form(...)):
    return {"account": account}

Uploading Files

Small files can be read entirely as bytes, while larger or stream-oriented uploads use UploadFile, which offers asynchronous methods and access to metadata.

from fastapi import File, UploadFile
from typing import List

@router.post("/upload/bytes")
def upload_bytes(raw: bytes = File(...)):
    return {"size_received": len(raw)}

@router.post("/upload/multiple")
async def upload_many(documents: List[UploadFile] = File(...)):
    for doc in documents:
        data = await doc.read()
        # process data
    first = documents[0]
    return {"name": first.filename, "mime": first.content_type}

Customizing Error Responses

Raise an HTTPException with a dedicated status code and optional headers to signal problems.

from fastapi import HTTPException

@router.get("/cities/{name}")
def lookup_city(name: str):
    if name != "Tokyo":
        raise HTTPException(status_code=404, detail="City unknown", headers={"X-Reason": "Not found"})
    return {"city": name}

Override the built-in handlers for HTTPException and RequestValidationError to return custom response formats.

from fastapi import Request
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as BaseHTTPException
from fastapi.exceptions import RequestValidationError

from main import app

@app.exception_handler(BaseHTTPException)
async def custom_http_handler(request: Request, exc: BaseHTTPException):
    return PlainTextResponse(f"Error: {exc.detail}", status_code=exc.status_code)

@app.exception_handler(RequestValidationError)
async def custom_validation_handler(request: Request, exc: RequestValidationError):
    return PlainTextResponse(f"Invalid input: {exc.errors()}", status_code=400)

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.