Ana içeriğe geç

Özel Response - HTML, Stream, File ve Diğerleri

🌐 Yapay Zekâ ve İnsanlar Tarafından Çeviri

Bu çeviri, insanlar tarafından yönlendirilen bir yapay zekâ ile oluşturuldu. 🤝

Orijinal anlamın yanlış anlaşılması ya da kulağa doğal gelmeme gibi hatalar içerebilir. 🤖

Yapay zekâ LLM'ini daha iyi yönlendirmemize yardımcı olarak bu çeviriyi iyileştirebilirsiniz.

İngilizce sürüm

Varsayılan olarak FastAPI, JSON response'lar döndürür.

Bunu, Doğrudan bir Response döndür bölümünde gördüğünüz gibi doğrudan bir Response döndürerek geçersiz kılabilirsiniz.

Ancak doğrudan bir Response döndürürseniz (veya JSONResponse gibi herhangi bir alt sınıfını), veri otomatik olarak dönüştürülmez (bir response_model tanımlamış olsanız bile) ve dokümantasyon da otomatik üretilmez (örneğin, üretilen OpenAPI’nin parçası olarak HTTP header Content-Type içindeki ilgili "media type" dahil edilmez).

Bununla birlikte, path operation decorator içinde response_class parametresini kullanarak hangi Response’un (örn. herhangi bir Response alt sınıfı) kullanılacağını da ilan edebilirsiniz.

path operation function’ınızdan döndürdüğünüz içerik, o Response’un içine yerleştirilir.

Not

Media type’ı olmayan bir response class kullanırsanız, FastAPI response’unuzun content içermediğini varsayar; bu yüzden ürettiği OpenAPI dokümanında response formatını dokümante etmez.

JSON Response'lar

Varsayılan olarak FastAPI JSON response'lar döndürür.

Bir Response Model tanımlarsanız, FastAPI veriyi Pydantic kullanarak JSON’a serialize eder.

Bir response modeli tanımlamazsanız, FastAPI JSON Compatible Encoder bölümünde açıklanan jsonable_encoder’ı kullanır ve sonucu bir JSONResponse içine koyar.

JSONResponse örneğinde olduğu gibi JSON media type’ına (application/json) sahip bir response_class tanımlarsanız, döndürdüğünüz veri; path operation decorator içinde tanımladığınız herhangi bir Pydantic response_model ile otomatik olarak dönüştürülür (ve filtrelenir). Ancak veri Pydantic ile JSON bytes’a serialize edilmez; bunun yerine jsonable_encoder ile dönüştürülür ve ardından Python’un standart JSON kütüphanesini kullanarak bytes’a serialize edecek olan JSONResponse class’ına iletilir.

JSON Performansı

Kısaca, en yüksek performansı istiyorsanız bir Response Model kullanın ve path operation decorator içinde response_class tanımlamayın.

# Code above omitted 👆

@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item

# Code below omitted 👇
👀 Full file preview
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: list[str] = []


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item


@app.get("/items/")
async def read_items() -> list[Item]:
    return [
        Item(name="Portal Gun", price=42.0),
        Item(name="Plumbus", price=32.0),
    ]

HTML Response

FastAPI’den doğrudan HTML içeren bir response döndürmek için HTMLResponse kullanın.

  • HTMLResponse import edin.
  • path operation decorator’ınızın response_class parametresi olarak HTMLResponse verin.
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>
    """

Bilgi

response_class parametresi, response’un "media type"’ını tanımlamak için de kullanılır.

Bu durumda HTTP header Content-Type, text/html olarak ayarlanır.

Ve OpenAPI’de de bu şekilde dokümante edilir.

Bir Response Döndür

Doğrudan bir Response döndür bölümünde görüldüğü gibi, path operation içinde doğrudan bir response döndürerek response’u override edebilirsiniz.

Yukarıdaki örneğin aynısı, bu sefer bir HTMLResponse döndürerek, şöyle görünebilir:

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)

Uyarı

path operation function’ınızın doğrudan döndürdüğü bir Response, OpenAPI’de dokümante edilmez (örneğin Content-Type dokümante edilmez) ve otomatik interaktif dokümanlarda görünmez.

Bilgi

Elbette gerçek Content-Type header’ı, status code vb. değerler, döndürdüğünüz Response objesinden gelir.

OpenAPI’de Dokümante Et ve Response’u Override Et

Response’u fonksiyonun içinden override etmek ama aynı zamanda OpenAPI’de "media type"’ı dokümante etmek istiyorsanız, response_class parametresini kullanıp ayrıca bir Response objesi döndürebilirsiniz.

Bu durumda response_class sadece OpenAPI path operation’ını dokümante etmek için kullanılır; sizin Response’unuz ise olduğu gibi kullanılır.

Doğrudan bir HTMLResponse Döndür

Örneğin şöyle bir şey olabilir:

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

Bu örnekte generate_html_response() fonksiyonu, HTML’i bir str olarak döndürmek yerine zaten bir Response üretip döndürmektedir.

generate_html_response() çağrısının sonucunu döndürerek, varsayılan FastAPI davranışını override edecek bir Response döndürmüş olursunuz.

Ama response_class içinde HTMLResponse da verdiğiniz için FastAPI, bunu OpenAPI’de ve interaktif dokümanlarda text/html ile HTML olarak nasıl dokümante edeceğini bilir:

Mevcut Response'lar

Mevcut response'lardan bazıları aşağıdadır.

Unutmayın: Response ile başka herhangi bir şeyi döndürebilir, hatta özel bir alt sınıf da oluşturabilirsiniz.

Teknik Detaylar

from starlette.responses import HTMLResponse da kullanabilirsiniz.

FastAPI, geliştirici için kolaylık olsun diye starlette.responses içindekileri fastapi.responses olarak da sağlar. Ancak mevcut response'ların çoğu doğrudan Starlette’ten gelir.

Response

Ana Response class’ıdır; diğer tüm response'lar bundan türetilir.

Bunu doğrudan döndürebilirsiniz.

Şu parametreleri kabul eder:

  • content - Bir str veya bytes.
  • status_code - Bir int HTTP status code.
  • headers - String’lerden oluşan bir dict.
  • media_type - Media type’ı veren bir str. Örn. "text/html".

FastAPI (aslında Starlette) otomatik olarak bir Content-Length header’ı ekler. Ayrıca media_type’a göre bir Content-Type header’ı ekler ve text türleri için sona bir charset ekler.

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

Yukarıda okuduğunuz gibi, bir miktar text veya bytes alır ve HTML response döndürür.

PlainTextResponse

Bir miktar text veya bytes alır ve düz metin response döndürür.

from fastapi import FastAPI
from fastapi.responses import PlainTextResponse

app = FastAPI()


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

JSONResponse

Bir miktar veri alır ve application/json olarak encode edilmiş bir response döndürür.

Yukarıda okuduğunuz gibi, FastAPI’de varsayılan response budur.

Teknik Detaylar

Ancak bir response modeli veya dönüş tipi (return type) tanımlarsanız, veri doğrudan JSON’a serialize edilir ve JSON için doğru media type’a sahip bir response, JSONResponse class’ı kullanılmadan doğrudan döndürülür.

Bu, en iyi performansı elde etmenin ideal yoludur.

RedirectResponse

HTTP redirect döndürür. Varsayılan olarak 307 status code (Temporary Redirect) kullanır.

RedirectResponse’u doğrudan döndürebilirsiniz:

from fastapi import FastAPI
from fastapi.responses import RedirectResponse

app = FastAPI()


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

Veya response_class parametresi içinde kullanabilirsiniz:

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"

Bunu yaparsanız, path operation function’ınızdan doğrudan URL döndürebilirsiniz.

Bu durumda kullanılan status_code, RedirectResponse için varsayılan olan 307 olur.


Ayrıca status_code parametresini response_class parametresiyle birlikte kullanabilirsiniz:

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

Bir async generator veya normal generator/iterator (içinde yield olan bir fonksiyon) alır ve response body’yi stream eder.

import anyio
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"
        await anyio.sleep(0)


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

Teknik Detaylar

Bir async görev, yalnızca bir await noktasına geldiğinde iptal edilebilir. Eğer hiç await yoksa, generator (içinde yield olan fonksiyon) düzgün şekilde iptal edilemez ve iptal istendikten sonra bile çalışmaya devam edebilir.

Bu küçük örneğin await ifadesine ihtiyacı olmadığı için, event loop’un iptali ele alabilmesi adına await anyio.sleep(0) ekliyoruz.

Büyük veya sonsuz akışlarda bu daha da önemlidir.

İpucu

Doğrudan bir StreamingResponse döndürmek yerine, muhtemelen Veri Stream Etme bölümündeki tarzı takip etmelisiniz; çok daha kullanışlıdır ve iptali arka planda sizin için halleder.

JSON Lines stream ediyorsanız, JSON Lines Stream Etme kılavuzunu izleyin.

FileResponse

Asenkron olarak bir dosyayı response olarak stream eder.

Diğer response türlerine göre instantiate ederken farklı argümanlar alır:

  • path - Stream edilecek dosyanın dosya path'i.
  • headers - Eklenecek özel header’lar; dictionary olarak.
  • media_type - Media type’ı veren string. Ayarlanmazsa, dosya adı veya path kullanılarak media type tahmin edilir.
  • filename - Ayarlanırsa response içindeki Content-Disposition’a dahil edilir.

File response'ları uygun Content-Length, Last-Modified ve ETag header’larını içerir.

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)

response_class parametresini de kullanabilirsiniz:

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

Bu durumda path operation function’ınızdan doğrudan dosya path'ini döndürebilirsiniz.

Özel response class

Response’dan türeterek kendi özel response class’ınızı oluşturabilir ve kullanabilirsiniz.

Örneğin, bazı ayarlarla orjson kullanmak istediğinizi varsayalım.

Diyelim ki girintili ve biçimlendirilmiş JSON döndürmek istiyorsunuz; bunun için orjson seçeneği orjson.OPT_INDENT_2’yi kullanmak istiyorsunuz.

Bir CustomORJSONResponse oluşturabilirsiniz. Burada yapmanız gereken temel şey, content’i bytes olarak döndüren bir Response.render(content) metodu yazmaktır:

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

Artık şunu döndürmek yerine:

{"message": "Hello World"}

...bu response şunu döndürür:

{
  "message": "Hello World"
}

Elbette JSON’u formatlamaktan çok daha iyi şekillerde bundan faydalanabilirsiniz. 😉

orjson mı Response Model mi

Aradığınız şey performans ise, büyük olasılıkla bir orjson response’tan ziyade bir Response Model kullanmak daha iyi olacaktır.

Bir response modeliyle FastAPI, veriyi JSON’a serialize etmek için Pydantic’i kullanır; böylece diğer durumlarda olacağı gibi jsonable_encoder ile ara dönüşümlere gerek kalmaz.

Ve kaputun altında, Pydantic JSON’a serialize etmek için orjson ile aynı Rust tabanlı mekanizmaları kullanır; bu nedenle bir response modeliyle zaten en iyi performansı elde edersiniz.

Varsayılan response class

Bir FastAPI class instance’ı veya bir APIRouter oluştururken, varsayılan olarak hangi response class’ının kullanılacağını belirtebilirsiniz.

Bunu tanımlayan parametre default_response_class’tır.

Aşağıdaki örnekte FastAPI, tüm path operations için varsayılan olarak JSON yerine HTMLResponse kullanır.

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI(default_response_class=HTMLResponse)


@app.get("/items/")
async def read_items():
    return "<h1>Items</h1><p>This is a list of items.</p>"

İpucu

Daha önce olduğu gibi, path operations içinde response_class’ı yine override edebilirsiniz.

Ek dokümantasyon

OpenAPI’de media type’ı ve daha birçok detayı responses kullanarak da tanımlayabilirsiniz: OpenAPI’de Ek Response'lar.