Skip to content

Custom APIRoute classes with explicit constructors fail after strict_content_type was added #15503

@4x4550x

Description

@4x4550x

Summary

Recent FastAPI versions added strict_content_type and now pass it through APIRouter.add_api_route() into the configured route_class. This breaks custom APIRoute subclasses that define an explicit constructor matching the previous public APIRoute.__init__ signature.

Subclasses that only override get_route_handler() still work, and subclasses using **kwargs still work. The regression affects subclasses that pinned the previously accepted constructor parameters and delegate them to super().__init__().

Reproducer

from enum import Enum
from typing import Any, Callable, Sequence

from fastapi import APIRouter, FastAPI, params
from fastapi.datastructures import Default, DefaultPlaceholder
from fastapi.responses import JSONResponse, Response
from fastapi.routing import APIRoute
from fastapi.types import IncEx
from fastapi.utils import generate_unique_id
from starlette.routing import BaseRoute


class LegacyRoute(APIRoute):
    def __init__(
        self,
        path: str,
        endpoint: Callable[..., Any],
        *,
        response_model: Any = Default(None),
        status_code: int | None = None,
        tags: list[str | Enum] | None = None,
        dependencies: Sequence[params.Depends] | None = None,
        summary: str | None = None,
        description: str | None = None,
        response_description: str = "Successful Response",
        responses: dict[int | str, dict[str, Any]] | None = None,
        deprecated: bool | None = None,
        name: str | None = None,
        methods: set[str] | list[str] | None = None,
        operation_id: str | None = None,
        response_model_include: IncEx | None = None,
        response_model_exclude: IncEx | None = None,
        response_model_by_alias: bool = True,
        response_model_exclude_unset: bool = False,
        response_model_exclude_defaults: bool = False,
        response_model_exclude_none: bool = False,
        include_in_schema: bool = True,
        response_class: type[Response] | DefaultPlaceholder = Default(JSONResponse),
        dependency_overrides_provider: Any | None = None,
        callbacks: list[BaseRoute] | None = None,
        openapi_extra: dict[str, Any] | None = None,
        generate_unique_id_function: Callable[[APIRoute], str]
        | DefaultPlaceholder = Default(generate_unique_id),
    ) -> None:
        super().__init__(
            path,
            endpoint,
            response_model=response_model,
            status_code=status_code,
            tags=tags,
            dependencies=dependencies,
            summary=summary,
            description=description,
            response_description=response_description,
            responses=responses,
            deprecated=deprecated,
            name=name,
            methods=methods,
            operation_id=operation_id,
            response_model_include=response_model_include,
            response_model_exclude=response_model_exclude,
            response_model_by_alias=response_model_by_alias,
            response_model_exclude_unset=response_model_exclude_unset,
            response_model_exclude_defaults=response_model_exclude_defaults,
            response_model_exclude_none=response_model_exclude_none,
            include_in_schema=include_in_schema,
            response_class=response_class,
            dependency_overrides_provider=dependency_overrides_provider,
            callbacks=callbacks,
            openapi_extra=openapi_extra,
            generate_unique_id_function=generate_unique_id_function,
        )


app = FastAPI()
router = APIRouter(route_class=LegacyRoute)


@router.get("/items")
def read_items():
    return {"ok": True}


app.include_router(router)

Current Behavior

Route registration fails with:

TypeError: LegacyRoute.__init__() got an unexpected keyword argument 'strict_content_type'

Expected Behavior

Existing custom route classes that mirrored the previous APIRoute.__init__ signature should continue to work, or FastAPI should make this breaking change explicit and document that custom route constructors must accept future keyword arguments.

Environment

  • FastAPI: current master at e89a37e50
  • Python: 3.13
  • Starlette: 1.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions