Aller au contenu

Réponse personnalisée - HTML, flux, fichier, autres

🌐 Traduction par IA et humains

Cette traduction a été réalisée par une IA guidée par des humains. 🤝

Elle peut contenir des erreurs d'interprétation du sens original, ou paraître peu naturelle, etc. 🤖

Vous pouvez améliorer cette traduction en nous aidant à mieux guider le LLM d'IA.

Version anglaise

Par défaut, FastAPI renverra les réponses en utilisant JSONResponse.

Vous pouvez le remplacer en renvoyant directement une Response comme expliqué dans Renvoyer directement une Response.

Mais si vous renvoyez directement une Response (ou n'importe quelle sous-classe, comme JSONResponse), les données ne seront pas automatiquement converties (même si vous déclarez un response_model), et la documentation ne sera pas générée automatiquement (par exemple, l'inclusion du « media type » dans l'en-tête HTTP Content-Type comme partie de l'OpenAPI généré).

Vous pouvez aussi déclarer la Response que vous voulez utiliser (par ex. toute sous-classe de Response), dans le décorateur de chemin d'accès en utilisant le paramètre response_class.

Le contenu que vous renvoyez depuis votre fonction de chemin d'accès sera placé à l'intérieur de cette Response.

Et si cette Response a un « media type » JSON (application/json), comme c'est le cas avec JSONResponse et UJSONResponse, les données que vous renvoyez seront automatiquement converties (et filtrées) avec tout response_model Pydantic que vous avez déclaré dans le décorateur de chemin d'accès.

Remarque

Si vous utilisez une classe de réponse sans « media type », FastAPI s'attendra à ce que votre réponse n'ait pas de contenu ; il ne documentera donc pas le format de la réponse dans les documents OpenAPI générés.

Utiliser ORJSONResponse

Par exemple, si vous cherchez à maximiser la performance, vous pouvez installer et utiliser orjson et définir la réponse sur ORJSONResponse.

Importez la classe (sous-classe) Response que vous voulez utiliser et déclarez-la dans le décorateur de chemin d'accès.

Pour de grandes réponses, renvoyer directement une Response est bien plus rapide que de renvoyer un dictionnaire.

Cela vient du fait que, par défaut, FastAPI inspectera chaque élément et s'assurera qu'il est sérialisable en JSON, en utilisant le même Encodeur compatible JSON expliqué dans le didacticiel. C'est ce qui vous permet de renvoyer des objets arbitraires, par exemple des modèles de base de données.

Mais si vous êtes certain que le contenu que vous renvoyez est sérialisable en JSON, vous pouvez le passer directement à la classe de réponse et éviter le surcoût supplémentaire qu'aurait FastAPI en faisant passer votre contenu de retour par le jsonable_encoder avant de le transmettre à la classe de réponse.

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI()


@app.get("/items/", response_class=ORJSONResponse)
async def read_items():
    return ORJSONResponse([{"item_id": "Foo"}])
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI()


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

Info

Le paramètre response_class sera aussi utilisé pour définir le « media type » de la réponse.

Dans ce cas, l'en-tête HTTP Content-Type sera défini à application/json.

Et il sera documenté comme tel dans OpenAPI.

Astuce

ORJSONResponse est disponible uniquement dans FastAPI, pas dans Starlette.

Réponse HTML

Pour renvoyer une réponse avec du HTML directement depuis FastAPI, utilisez HTMLResponse.

  • Importez HTMLResponse.
  • Passez HTMLResponse comme paramètre response_class de votre décorateur de chemin d'accès.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """

Info

Le paramètre response_class sera aussi utilisé pour définir le « media type » de la réponse.

Dans ce cas, l'en-tête HTTP Content-Type sera défini à text/html.

Et il sera documenté comme tel dans OpenAPI.

Renvoyer une Response

Comme vu dans Renvoyer directement une Response, vous pouvez aussi remplacer la réponse directement dans votre chemin d'accès, en la renvoyant.

Le même exemple ci-dessus, renvoyant une HTMLResponse, pourrait ressembler à :

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/")
async def read_items():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.get("/items/")
async def read_items():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)

Alertes

Une Response renvoyée directement par votre fonction de chemin d'accès ne sera pas documentée dans OpenAPI (par exemple, le Content-Type ne sera pas documenté) et ne sera pas visible dans les documents interactifs automatiques.

Info

Bien sûr, l'en-tête Content-Type réel, le code d'état, etc., proviendront de l'objet Response que vous avez renvoyé.

Documenter dans OpenAPI et remplacer Response

Si vous voulez remplacer la réponse depuis l'intérieur de la fonction mais en même temps documenter le « media type » dans OpenAPI, vous pouvez utiliser le paramètre response_class ET renvoyer un objet Response.

response_class sera alors utilisé uniquement pour documenter l'opération de chemin d'accès OpenAPI, mais votre Response sera utilisée telle quelle.

Renvoyer directement une HTMLResponse

Par exemple, cela pourrait être quelque chose comme :

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


def generate_html_response():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return generate_html_response()
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()


def generate_html_response():
    html_content = """
    <html>
        <head>
            <title>Some HTML in here</title>
        </head>
        <body>
            <h1>Look ma! HTML!</h1>
        </body>
    </html>
    """
    return HTMLResponse(content=html_content, status_code=200)


@app.get("/items/", response_class=HTMLResponse)
async def read_items():
    return generate_html_response()

Dans cet exemple, la fonction generate_html_response() génère déjà et renvoie une Response au lieu de renvoyer le HTML dans une str.

En renvoyant le résultat de l'appel à generate_html_response(), vous renvoyez déjà une Response qui remplacera le comportement par défaut de FastAPI.

Mais comme vous avez aussi passé HTMLResponse dans response_class, FastAPI saura comment la documenter dans OpenAPI et les documents interactifs comme HTML avec text/html :

Réponses disponibles

Voici certaines des réponses disponibles.

Gardez à l'esprit que vous pouvez utiliser Response pour renvoyer autre chose, ou même créer une sous-classe personnalisée.

Détails techniques

Vous pourriez aussi utiliser from starlette.responses import HTMLResponse.

FastAPI fournit les mêmes starlette.responses sous fastapi.responses simplement pour votre confort de développement. Mais la plupart des réponses disponibles viennent directement de Starlette.

Response

La classe principale Response, toutes les autres réponses en héritent.

Vous pouvez la renvoyer directement.

Elle accepte les paramètres suivants :

  • content - Une str ou des bytes.
  • status_code - Un code d'état HTTP de type int.
  • headers - Un dict de chaînes.
  • media_type - Une str donnant le media type. Par exemple « text/html ».

FastAPI (en fait Starlette) inclura automatiquement un en-tête Content-Length. Il inclura aussi un en-tête Content-Type, basé sur media_type et en ajoutant un charset pour les types textuels.

from fastapi import FastAPI, Response

app = FastAPI()


@app.get("/legacy/")
def get_legacy_data():
    data = """<?xml version="1.0"?>
    <shampoo>
    <Header>
        Apply shampoo here.
    </Header>
    <Body>
        You'll have to use soap here.
    </Body>
    </shampoo>
    """
    return Response(content=data, media_type="application/xml")
🤓 Other versions and variants
from fastapi import FastAPI, Response

app = FastAPI()


@app.get("/legacy/")
def get_legacy_data():
    data = """<?xml version="1.0"?>
    <shampoo>
    <Header>
        Apply shampoo here.
    </Header>
    <Body>
        You'll have to use soap here.
    </Body>
    </shampoo>
    """
    return Response(content=data, media_type="application/xml")

HTMLResponse

Prend du texte ou des octets et renvoie une réponse HTML, comme vous l'avez lu ci-dessus.

PlainTextResponse

Prend du texte ou des octets et renvoie une réponse en texte brut.

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


@app.get("/", response_class=PlainTextResponse)
async def main():
    return "Hello World"
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


@app.get("/", response_class=PlainTextResponse)
async def main():
    return "Hello World"

JSONResponse

Prend des données et renvoie une réponse encodée en application/json.

C'est la réponse par défaut utilisée dans FastAPI, comme vous l'avez lu ci-dessus.

ORJSONResponse

Une réponse JSON alternative rapide utilisant orjson, comme vous l'avez lu ci-dessus.

Info

Cela nécessite l'installation de orjson, par exemple avec pip install orjson.

UJSONResponse

Une réponse JSON alternative utilisant ujson.

Info

Cela nécessite l'installation de ujson, par exemple avec pip install ujson.

Alertes

ujson est moins rigoureux que l'implémentation intégrée de Python dans sa gestion de certains cas limites.

from fastapi import FastAPI
from fastapi.responses import UJSONResponse

app = FastAPI()


@app.get("/items/", response_class=UJSONResponse)
async def read_items():
    return [{"item_id": "Foo"}]
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import UJSONResponse

app = FastAPI()


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

Astuce

Il est possible que ORJSONResponse soit une alternative plus rapide.

RedirectResponse

Renvoie une redirection HTTP. Utilise par défaut un code d'état 307 (Temporary Redirect).

Vous pouvez renvoyer directement une RedirectResponse :

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/typer")
async def redirect_typer():
    return RedirectResponse("https://typer.tiangolo.com")
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/typer")
async def redirect_typer():
    return RedirectResponse("https://typer.tiangolo.com")

Ou vous pouvez l'utiliser dans le paramètre response_class :

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
    return "https://fastapi.tiangolo.com"
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/fastapi", response_class=RedirectResponse)
async def redirect_fastapi():
    return "https://fastapi.tiangolo.com"

Si vous faites cela, vous pouvez alors renvoyer directement l'URL depuis votre fonction de chemin d'accès.

Dans ce cas, le status_code utilisé sera celui par défaut pour RedirectResponse, c'est-à-dire 307.


Vous pouvez aussi utiliser le paramètre status_code combiné avec le paramètre response_class :

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
    return "https://docs.pydantic.dev/"
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


@app.get("/pydantic", response_class=RedirectResponse, status_code=302)
async def redirect_pydantic():
    return "https://docs.pydantic.dev/"

StreamingResponse

Prend un générateur async ou un générateur/itérateur normal et diffuse le corps de la réponse.

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()


async def fake_video_streamer():
    for i in range(10):
        yield b"some fake video bytes"


@app.get("/")
async def main():
    return StreamingResponse(fake_video_streamer())
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()


async def fake_video_streamer():
    for i in range(10):
        yield b"some fake video bytes"


@app.get("/")
async def main():
    return StreamingResponse(fake_video_streamer())

Utiliser StreamingResponse avec des objets de type fichier

Si vous avez un objet de type fichier (par ex. l'objet renvoyé par open()), vous pouvez créer une fonction génératrice pour itérer sur cet objet de type fichier.

De cette façon, vous n'avez pas à tout lire en mémoire au préalable, et vous pouvez passer cette fonction génératrice à StreamingResponse, puis la renvoyer.

Cela inclut de nombreuses bibliothèques pour interagir avec du stockage cloud, du traitement vidéo, et autres.

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
def main():
    def iterfile():  # (1)
        with open(some_file_path, mode="rb") as file_like:  # (2)
            yield from file_like  # (3)

    return StreamingResponse(iterfile(), media_type="video/mp4")
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
def main():
    def iterfile():  # (1)
        with open(some_file_path, mode="rb") as file_like:  # (2)
            yield from file_like  # (3)

    return StreamingResponse(iterfile(), media_type="video/mp4")
  1. C'est la fonction génératrice. C'est une « fonction génératrice » parce qu'elle contient des instructions yield à l'intérieur.
  2. En utilisant un bloc with, nous nous assurons que l'objet de type fichier est fermé après l'exécution de la fonction génératrice. Donc, après qu'elle a fini d'envoyer la réponse.
  3. Ce yield from indique à la fonction d'itérer sur l'objet nommé file_like. Puis, pour chaque partie itérée, de produire cette partie comme provenant de cette fonction génératrice (iterfile).

    Ainsi, c'est une fonction génératrice qui transfère le travail de « génération » à autre chose en interne.

    En procédant ainsi, nous pouvons la placer dans un bloc with et, de cette façon, garantir que l'objet de type fichier est fermé après la fin.

Astuce

Remarquez qu'ici, comme nous utilisons le open() standard qui ne prend pas en charge async et await, nous déclarons le chemin d'accès avec un def normal.

FileResponse

Diffuse de façon asynchrone un fichier comme réponse.

Prend un ensemble de paramètres différent à l'instanciation par rapport aux autres types de réponse :

  • path - Le chemin du fichier à diffuser.
  • headers - D'éventuels en-têtes personnalisés à inclure, sous forme de dictionnaire.
  • media_type - Une chaîne donnant le media type. Si non défini, le nom du fichier ou le chemin sera utilisé pour en déduire un media type.
  • filename - Si défini, sera inclus dans l'en-tête Content-Disposition de la réponse.

Les réponses de type fichier incluront les en-têtes appropriés Content-Length, Last-Modified et ETag.

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
async def main():
    return FileResponse(some_file_path)
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/")
async def main():
    return FileResponse(some_file_path)

Vous pouvez aussi utiliser le paramètre response_class :

from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/", response_class=FileResponse)
async def main():
    return some_file_path
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import FileResponse

some_file_path = "large-video-file.mp4"
app = FastAPI()


@app.get("/", response_class=FileResponse)
async def main():
    return some_file_path

Dans ce cas, vous pouvez renvoyer directement le chemin du fichier depuis votre fonction de chemin d'accès.

Classe de réponse personnalisée

Vous pouvez créer votre propre classe de réponse personnalisée, héritant de Response, et l'utiliser.

Par exemple, disons que vous voulez utiliser orjson, mais avec certains réglages personnalisés non utilisés dans la classe ORJSONResponse incluse.

Disons que vous voulez renvoyer du JSON indenté et formaté, donc vous voulez utiliser l'option orjson orjson.OPT_INDENT_2.

Vous pourriez créer une CustomORJSONResponse. L'essentiel est de créer une méthode Response.render(content) qui renvoie le contenu en bytes :

from typing import Any

import orjson
from fastapi import FastAPI, Response

app = FastAPI()


class CustomORJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
        assert orjson is not None, "orjson must be installed"
        return orjson.dumps(content, option=orjson.OPT_INDENT_2)


@app.get("/", response_class=CustomORJSONResponse)
async def main():
    return {"message": "Hello World"}
🤓 Other versions and variants
from typing import Any

import orjson
from fastapi import FastAPI, Response

app = FastAPI()


class CustomORJSONResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
        assert orjson is not None, "orjson must be installed"
        return orjson.dumps(content, option=orjson.OPT_INDENT_2)


@app.get("/", response_class=CustomORJSONResponse)
async def main():
    return {"message": "Hello World"}

Maintenant, au lieu de renvoyer :

{"message": "Hello World"}

... cette réponse renverra :

{
  "message": "Hello World"
}

Bien sûr, vous trouverez probablement des moyens bien meilleurs de tirer parti de cela que de formater du JSON. 😉

Classe de réponse par défaut

Lors de la création d'une instance de classe FastAPI ou d'un APIRouter, vous pouvez spécifier quelle classe de réponse utiliser par défaut.

Le paramètre qui le définit est default_response_class.

Dans l'exemple ci-dessous, FastAPI utilisera ORJSONResponse par défaut, dans tous les chemins d'accès, au lieu de JSONResponse.

from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI(default_response_class=ORJSONResponse)


@app.get("/items/")
async def read_items():
    return [{"item_id": "Foo"}]
🤓 Other versions and variants
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI(default_response_class=ORJSONResponse)


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

Astuce

Vous pouvez toujours remplacer response_class dans les chemins d'accès comme auparavant.

Documentation supplémentaire

Vous pouvez aussi déclarer le media type et de nombreux autres détails dans OpenAPI en utilisant responses : Réponses supplémentaires dans OpenAPI.