Aller au contenu

Configuration avancée des chemins d'accès

🌐 Translation by AI and humans

This translation was made by AI guided by humans. 🤝

It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖

You can improve this translation by helping us guide the AI LLM better.

English version

ID d’opération OpenAPI

Alertes

Si vous n’êtes pas un « expert » d’OpenAPI, vous n’en avez probablement pas besoin.

Vous pouvez définir l’OpenAPI operationId à utiliser dans votre chemin d’accès avec le paramètre operation_id.

Vous devez vous assurer qu’il est unique pour chaque opération.

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", operation_id="some_specific_id_you_define")
async def read_items():
    return [{"item_id": "Foo"}]

Utiliser le nom de la fonction de chemin d’accès comme operationId

Si vous souhaitez utiliser les noms de fonction de vos API comme operationId, vous pouvez les parcourir tous et remplacer l’operation_id de chaque chemin d’accès en utilisant leur APIRoute.name.

Vous devez le faire après avoir ajouté tous vos chemins d’accès.

from fastapi import FastAPI
from fastapi.routing import APIRoute

app = FastAPI()


@app.get("/items/")
async def read_items():
    return [{"item_id": "Foo"}]


def use_route_names_as_operation_ids(app: FastAPI) -> None:
    """
    Simplify operation IDs so that generated API clients have simpler function
    names.

    Should be called only after all routes have been added.
    """
    for route in app.routes:
        if isinstance(route, APIRoute):
            route.operation_id = route.name  # in this case, 'read_items'


use_route_names_as_operation_ids(app)

Astuce

Si vous appelez manuellement app.openapi(), vous devez mettre à jour les operationId avant cela.

Alertes

Si vous faites cela, vous devez vous assurer que chacune de vos fonctions de chemin d’accès a un nom unique.

Même si elles se trouvent dans des modules différents (fichiers Python).

Exclusion d’OpenAPI

Pour exclure un chemin d’accès du schéma OpenAPI généré (et donc des systèmes de documentation automatiques), utilisez le paramètre include_in_schema et définissez-le à False :

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", include_in_schema=False)
async def read_items():
    return [{"item_id": "Foo"}]

Description avancée depuis la docstring

Vous pouvez limiter les lignes utilisées de la docstring d’une fonction de chemin d’accès pour OpenAPI.

L’ajout d’un \f (un caractère « saut de page » échappé) amène FastAPI à tronquer la sortie utilisée pour OpenAPI à cet endroit.

Cela n’apparaîtra pas dans la documentation, mais d’autres outils (comme Sphinx) pourront utiliser le reste.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()


@app.post("/items/", summary="Create an item")
async def create_item(item: Item) -> Item:
    """
    Create an item with all the information:

    - **name**: each item must have a name
    - **description**: a long description
    - **price**: required
    - **tax**: if the item doesn't have tax, you can omit this
    - **tags**: a set of unique tag strings for this item
    \f
    :param item: User input.
    """
    return item
🤓 Other versions and variants
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: set[str] = set()


@app.post("/items/", summary="Create an item")
async def create_item(item: Item) -> Item:
    """
    Create an item with all the information:

    - **name**: each item must have a name
    - **description**: a long description
    - **price**: required
    - **tax**: if the item doesn't have tax, you can omit this
    - **tags**: a set of unique tag strings for this item
    \f
    :param item: User input.
    """
    return item

Réponses supplémentaires

Vous avez probablement vu comment déclarer le response_model et le status_code pour un chemin d’accès.

Cela définit les métadonnées sur la réponse principale d’un chemin d’accès.

Vous pouvez également déclarer des réponses supplémentaires avec leurs modèles, codes de statut, etc.

Il y a un chapitre entier dans la documentation à ce sujet, vous pouvez le lire dans Réponses supplémentaires dans OpenAPI.

OpenAPI supplémentaire

Lorsque vous déclarez un chemin d’accès dans votre application, FastAPI génère automatiquement les métadonnées pertinentes à propos de ce chemin d’accès à inclure dans le schéma OpenAPI.

Détails techniques

Dans la spécification OpenAPI, cela s’appelle l’objet Operation.

Il contient toutes les informations sur le chemin d’accès et est utilisé pour générer la documentation automatique.

Il inclut les tags, parameters, requestBody, responses, etc.

Ce schéma OpenAPI spécifique à un chemin d’accès est normalement généré automatiquement par FastAPI, mais vous pouvez également l’étendre.

Astuce

Ceci est un point d’extension de bas niveau.

Si vous avez seulement besoin de déclarer des réponses supplémentaires, un moyen plus pratique de le faire est d’utiliser Réponses supplémentaires dans OpenAPI.

Vous pouvez étendre le schéma OpenAPI pour un chemin d’accès en utilisant le paramètre openapi_extra.

Extensions OpenAPI

Cet openapi_extra peut être utile, par exemple, pour déclarer des Extensions OpenAPI :

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"})
async def read_items():
    return [{"item_id": "portal-gun"}]

Si vous ouvrez la documentation automatique de l’API, votre extension apparaîtra en bas du chemin d’accès spécifique.

Et si vous consultez l’OpenAPI résultant (à /openapi.json dans votre API), vous verrez également votre extension comme partie du chemin d’accès spécifique :

{
    "openapi": "3.1.0",
    "info": {
        "title": "FastAPI",
        "version": "0.1.0"
    },
    "paths": {
        "/items/": {
            "get": {
                "summary": "Read Items",
                "operationId": "read_items_items__get",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    }
                },
                "x-aperture-labs-portal": "blue"
            }
        }
    }
}

Personnaliser le schéma OpenAPI d’un chemin d’accès

Le dictionnaire dans openapi_extra sera fusionné en profondeur avec le schéma OpenAPI généré automatiquement pour le chemin d’accès.

Ainsi, vous pouvez ajouter des données supplémentaires au schéma généré automatiquement.

Par exemple, vous pourriez décider de lire et de valider la requête avec votre propre code, sans utiliser les fonctionnalités automatiques de FastAPI avec Pydantic, mais vous pourriez tout de même vouloir définir la requête dans le schéma OpenAPI.

Vous pourriez le faire avec openapi_extra :

from fastapi import FastAPI, Request

app = FastAPI()


def magic_data_reader(raw_body: bytes):
    return {
        "size": len(raw_body),
        "content": {
            "name": "Maaaagic",
            "price": 42,
            "description": "Just kiddin', no magic here. ✨",
        },
    }


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {
                "application/json": {
                    "schema": {
                        "required": ["name", "price"],
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "price": {"type": "number"},
                            "description": {"type": "string"},
                        },
                    }
                }
            },
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    data = magic_data_reader(raw_body)
    return data

Dans cet exemple, nous n’avons déclaré aucun modèle Pydantic. En fait, le corps de la requête n’est même pas parsé en JSON, il est lu directement en tant que bytes, et la fonction magic_data_reader() serait chargée de l’analyser d’une manière ou d’une autre.

Néanmoins, nous pouvons déclarer le schéma attendu pour le corps de la requête.

Type de contenu OpenAPI personnalisé

En utilisant cette même astuce, vous pourriez utiliser un modèle Pydantic pour définir le JSON Schema qui est ensuite inclus dans la section de schéma OpenAPI personnalisée pour le chemin d’accès.

Et vous pourriez le faire même si le type de données dans la requête n’est pas du JSON.

Par exemple, dans cette application nous n’utilisons pas la fonctionnalité intégrée de FastAPI pour extraire le JSON Schema des modèles Pydantic ni la validation automatique pour le JSON. En fait, nous déclarons le type de contenu de la requête comme YAML, pas JSON :

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: list[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.model_validate(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors(include_url=False))
    return item

Néanmoins, bien que nous n’utilisions pas la fonctionnalité intégrée par défaut, nous utilisons toujours un modèle Pydantic pour générer manuellement le JSON Schema pour les données que nous souhaitons recevoir en YAML.

Ensuite, nous utilisons directement la requête et extrayons le corps en tant que bytes. Cela signifie que FastAPI n’essaiera même pas d’analyser le payload de la requête en JSON.

Ensuite, dans notre code, nous analysons directement ce contenu YAML, puis nous utilisons à nouveau le même modèle Pydantic pour valider le contenu YAML :

import yaml
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel, ValidationError

app = FastAPI()


class Item(BaseModel):
    name: str
    tags: list[str]


@app.post(
    "/items/",
    openapi_extra={
        "requestBody": {
            "content": {"application/x-yaml": {"schema": Item.model_json_schema()}},
            "required": True,
        },
    },
)
async def create_item(request: Request):
    raw_body = await request.body()
    try:
        data = yaml.safe_load(raw_body)
    except yaml.YAMLError:
        raise HTTPException(status_code=422, detail="Invalid YAML")
    try:
        item = Item.model_validate(data)
    except ValidationError as e:
        raise HTTPException(status_code=422, detail=e.errors(include_url=False))
    return item

Astuce

Ici, nous réutilisons le même modèle Pydantic.

Mais de la même manière, nous aurions pu le valider autrement.