Zum Inhalt

Benutzerdefinierte Response – HTML, Stream, Datei, andere

Standardmäßig gibt FastAPI die Responses mittels JSONResponse zurück.

Sie können das überschreiben, indem Sie direkt eine Response zurückgeben, wie in Eine Response direkt zurückgeben gezeigt.

Wenn Sie jedoch direkt eine Response zurückgeben, werden die Daten nicht automatisch konvertiert und die Dokumentation wird nicht automatisch generiert (zum Beispiel wird der spezifische „Medientyp“, der im HTTP-Header Content-Type angegeben ist, nicht Teil der generierten OpenAPI).

Sie können aber auch die Response, die Sie verwenden möchten, im Pfadoperation-Dekorator deklarieren.

Der Inhalt, den Sie von Ihrer Pfadoperation-Funktion zurückgeben, wird in diese Response eingefügt.

Und wenn diese Response einen JSON-Medientyp (application/json) hat, wie es bei JSONResponse und UJSONResponse der Fall ist, werden die von Ihnen zurückgegebenen Daten automatisch mit jedem Pydantic response_model konvertiert (und gefiltert), das Sie im Pfadoperation-Dekorator deklariert haben.

Hinweis

Wenn Sie eine Response-Klasse ohne Medientyp verwenden, erwartet FastAPI, dass Ihre Response keinen Inhalt hat, und dokumentiert daher das Format der Response nicht in deren generierter OpenAPI-Dokumentation.

ORJSONResponse verwenden

Um beispielsweise noch etwas Leistung herauszuholen, können Sie orjson installieren und verwenden, und die Response als ORJSONResponse deklarieren.

Importieren Sie die Response-Klasse (-Unterklasse), die Sie verwenden möchten, und deklarieren Sie sie im Pfadoperation-Dekorator.

Bei umfangreichen Responses ist die direkte Rückgabe einer Response viel schneller als ein Dictionary zurückzugeben.

Das liegt daran, dass FastAPI standardmäßig jedes enthaltene Element überprüft und sicherstellt, dass es als JSON serialisierbar ist, und zwar unter Verwendung desselben JSON-kompatiblen Encoders, der im Tutorial erläutert wurde. Dadurch können Sie beliebige Objekte zurückgeben, zum Beispiel Datenbankmodelle.

Wenn Sie jedoch sicher sind, dass der von Ihnen zurückgegebene Inhalt mit JSON serialisierbar ist, können Sie ihn direkt an die Response-Klasse übergeben und die zusätzliche Arbeit vermeiden, die FastAPI hätte, indem es Ihren zurückgegebenen Inhalt durch den jsonable_encoder leitet, bevor es ihn an die Response-Klasse übergibt.

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

Der Parameter response_class wird auch verwendet, um den „Medientyp“ der Response zu definieren.

In diesem Fall wird der HTTP-Header Content-Type auf application/json gesetzt.

Und er wird als solcher in OpenAPI dokumentiert.

Tipp

Die ORJSONResponse ist derzeit nur in FastAPI verfügbar, nicht in Starlette.

HTML-Response

Um eine Response mit HTML direkt von FastAPI zurückzugeben, verwenden Sie HTMLResponse.

  • Importieren Sie HTMLResponse.
  • Übergeben Sie HTMLResponse als den Parameter response_class Ihres Pfadoperation-Dekorators.
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

Der Parameter response_class wird auch verwendet, um den „Medientyp“ der Response zu definieren.

In diesem Fall wird der HTTP-Header Content-Type auf text/html gesetzt.

Und er wird als solcher in OpenAPI dokumentiert.

Eine Response zurückgeben

Wie in Eine Response direkt zurückgeben gezeigt, können Sie die Response auch direkt in Ihrer Pfadoperation überschreiben, indem Sie diese zurückgeben.

Das gleiche Beispiel von oben, das eine HTMLResponse zurückgibt, könnte so aussehen:

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)

Achtung

Eine Response, die direkt von Ihrer Pfadoperation-Funktion zurückgegeben wird, wird in OpenAPI nicht dokumentiert (zum Beispiel wird der Content-Type nicht dokumentiert) und ist in der automatischen interaktiven Dokumentation nicht sichtbar.

Info

Natürlich stammen der eigentliche Content-Type-Header, der Statuscode, usw., aus dem Response-Objekt, das Sie zurückgegeben haben.

In OpenAPI dokumentieren und Response überschreiben

Wenn Sie die Response innerhalb der Funktion überschreiben und gleichzeitig den „Medientyp“ in OpenAPI dokumentieren möchten, können Sie den response_class-Parameter verwenden UND ein Response-Objekt zurückgeben.

Die response_class wird dann nur zur Dokumentation der OpenAPI-Pfadoperation* verwendet, Ihre Response wird jedoch unverändert verwendet.

Eine HTMLResponse direkt zurückgeben

Es könnte zum Beispiel so etwas sein:

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()

In diesem Beispiel generiert die Funktion generate_html_response() bereits eine Response und gibt sie zurück, anstatt das HTML in einem str zurückzugeben.

Indem Sie das Ergebnis des Aufrufs von generate_html_response() zurückgeben, geben Sie bereits eine Response zurück, die das Standardverhalten von FastAPI überschreibt.

Aber da Sie die HTMLResponse auch in der response_class übergeben haben, weiß FastAPI, dass sie in OpenAPI und der interaktiven Dokumentation als HTML mit text/html zu dokumentieren ist:

Verfügbare Responses

Hier sind einige der verfügbaren Responses.

Bedenken Sie, dass Sie Response verwenden können, um alles andere zurückzugeben, oder sogar eine benutzerdefinierte Unterklasse zu erstellen.

Technische Details

Sie können auch from starlette.responses import HTMLResponse verwenden.

FastAPI bietet dieselben starlette.responses auch via fastapi.responses an, als Annehmlichkeit für Sie, den Entwickler. Die meisten verfügbaren Responses kommen aber direkt von Starlette.

Response

Die Hauptklasse Response, alle anderen Responses erben von ihr.

Sie können sie direkt zurückgeben.

Sie akzeptiert die folgenden Parameter:

  • content – Ein str oder bytes.
  • status_code – Ein int-HTTP-Statuscode.
  • headers – Ein dict von Strings.
  • media_type – Ein str, der den Medientyp angibt. Z. B. "text/html".

FastAPI (eigentlich Starlette) fügt automatisch einen Content-Length-Header ein. Außerdem wird es einen Content-Type-Header einfügen, der auf dem media_type basiert, und für Texttypen einen Zeichensatz (charset) anfügen.

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

Nimmt Text oder Bytes entgegen und gibt eine HTML-Response zurück, wie Sie oben gelesen haben.

PlainTextResponse

Nimmt Text oder Bytes entgegen und gibt eine Plain-Text-Response zurück.

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


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

JSONResponse

Nimmt einige Daten entgegen und gibt eine application/json-codierte Response zurück.

Dies ist die Standard-Response, die in FastAPI verwendet wird, wie Sie oben gelesen haben.

ORJSONResponse

Eine schnelle alternative JSON-Response mit orjson, wie Sie oben gelesen haben.

UJSONResponse

Eine alternative JSON-Response mit ujson.

Achtung

ujson ist bei der Behandlung einiger Sonderfälle weniger sorgfältig als Pythons eingebaute Implementierung.

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"}]

Tipp

Möglicherweise ist ORJSONResponse eine schnellere Alternative.

RedirectResponse

Gibt eine HTTP-Weiterleitung (HTTP-Redirect) zurück. Verwendet standardmäßig den Statuscode 307 – Temporäre Weiterleitung (Temporary Redirect).

Sie können eine RedirectResponse direkt zurückgeben:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


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

Oder Sie können sie im Parameter response_class verwenden:

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"

Wenn Sie das tun, können Sie die URL direkt von Ihrer Pfadoperation-Funktion zurückgeben.

In diesem Fall ist der verwendete status_code der Standardcode für die RedirectResponse, also 307.


Sie können den Parameter status_code auch in Kombination mit dem Parameter response_class verwenden:

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://pydantic-docs.helpmanual.io/"

StreamingResponse

Nimmt einen asynchronen Generator oder einen normalen Generator/Iterator und streamt den Responsebody.

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())

Verwendung von StreamingResponse mit dateiähnlichen Objekten

Wenn Sie ein dateiähnliches (file-like) Objekt haben (z. B. das von open() zurückgegebene Objekt), können Sie eine Generatorfunktion erstellen, um über dieses dateiähnliche Objekt zu iterieren.

Auf diese Weise müssen Sie nicht alles zuerst in den Arbeitsspeicher lesen und können diese Generatorfunktion an StreamingResponse übergeben und zurückgeben.

Das umfasst viele Bibliotheken zur Interaktion mit Cloud-Speicher, Videoverarbeitung und anderen.

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. Das ist die Generatorfunktion. Es handelt sich um eine „Generatorfunktion“, da sie yield-Anweisungen enthält.
  2. Durch die Verwendung eines with-Blocks stellen wir sicher, dass das dateiähnliche Objekt geschlossen wird, nachdem die Generatorfunktion fertig ist. Also, nachdem sie mit dem Senden der Response fertig ist.
  3. Dieses yield from weist die Funktion an, über das Ding namens file_like zu iterieren. Und dann für jeden iterierten Teil, diesen Teil so zurückzugeben, als wenn er aus dieser Generatorfunktion (iterfile) stammen würde.

    Es handelt sich also hier um eine Generatorfunktion, die die „generierende“ Arbeit intern auf etwas anderes überträgt.

    Auf diese Weise können wir das Ganze in einen with-Block einfügen und so sicherstellen, dass das dateiartige Objekt nach Abschluss geschlossen wird.

Tipp

Beachten Sie, dass wir, da wir Standard-open() verwenden, welches async und await nicht unterstützt, hier die Pfadoperation mit normalen def deklarieren.

FileResponse

Streamt eine Datei asynchron als Response.

Nimmt zur Instanziierung einen anderen Satz von Argumenten entgegen als die anderen Response-Typen:

  • path – Der Dateipfad zur Datei, die gestreamt werden soll.
  • headers – Alle benutzerdefinierten Header, die inkludiert werden sollen, als Dictionary.
  • media_type – Ein String, der den Medientyp angibt. Wenn nicht gesetzt, wird der Dateiname oder Pfad verwendet, um auf einen Medientyp zu schließen.
  • filename – Wenn gesetzt, wird das in der Content-Disposition der Response eingefügt.

Datei-Responses enthalten die entsprechenden Content-Length-, Last-Modified- und ETag-Header.

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)

Sie können auch den Parameter response_class verwenden:

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

In diesem Fall können Sie den Dateipfad direkt von Ihrer Pfadoperation-Funktion zurückgeben.

Benutzerdefinierte Response-Klasse

Sie können Ihre eigene benutzerdefinierte Response-Klasse erstellen, die von Response erbt und diese verwendet.

Nehmen wir zum Beispiel an, dass Sie orjson verwenden möchten, aber mit einigen benutzerdefinierten Einstellungen, die in der enthaltenen ORJSONResponse-Klasse nicht verwendet werden.

Sie möchten etwa, dass Ihre Response eingerücktes und formatiertes JSON zurückgibt. Dafür möchten Sie die orjson-Option orjson.OPT_INDENT_2 verwenden.

Sie könnten eine CustomORJSONResponse erstellen. Das Wichtigste, was Sie tun müssen, ist, eine Response.render(content)-Methode zu erstellen, die den Inhalt als bytes zurückgibt:

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"}

Statt:

{"message": "Hello World"}

... wird die Response jetzt Folgendes zurückgeben:

{
  "message": "Hello World"
}

Natürlich werden Sie wahrscheinlich viel bessere Möglichkeiten finden, Vorteil daraus zu ziehen, als JSON zu formatieren. 😉

Standard-Response-Klasse

Beim Erstellen einer FastAPI-Klasseninstanz oder eines APIRouters können Sie angeben, welche Response-Klasse standardmäßig verwendet werden soll.

Der Parameter, der das definiert, ist default_response_class.

Im folgenden Beispiel verwendet FastAPI standardmäßig ORJSONResponse in allen Pfadoperationen, anstelle von 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"}]

Tipp

Sie können dennoch weiterhin response_class in Pfadoperationen überschreiben, wie bisher.

Zusätzliche Dokumentation

Sie können auch den Medientyp und viele andere Details in OpenAPI mit responses deklarieren: Zusätzliche Responses in OpenAPI.