0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

(エラーメモ)CloudRunにデプロイしたFastAPIでレスポンスクラスの宣言が異なるときの「Service Unavailable response」について

Last updated at Posted at 2023-06-19

学び

それはそうだろという話ではあるけど、routeデコレーター引数のresponse_classをHTMLレスポンスで宣言しているにもかかわらず、実際はそのルータメソッドでFileResponseを返していると、HTTPレスポンスを上手くフォーマットできない場合がある。

今回Cloud Runでデプロイした場合は「Service Unavailable」が起きた。FastAPIもCloud RunもHTTP通信を明示的に扱うため、APIが宣言するものと実際に送信するものに不一致がある場合、予期せぬエラーにつながる。

この実装をした背景

  • FastAPIでフォーム送信すると、その内容に基づいて画像ファイルを生成して、ファイルの中身を画面で表示させるWebアプリを開発した(画面にはファイルの中身を表示させたいが、ファイルダウンロードさせたいわけではない。)
  • ローカルでの動作確認やGAEやGCEでデプロイしたときは、response_classとレスポンスが異なっていても、Webアプリはいい感じにファイルの中身を表示してくれていたので、この雑なやりかたでも問題なかった。
  • 例えばルータメソッドの中身はこんな感じ(エラーが起きる例):
@router.post(
    f"***",
    response_class=HTMLResponse,
    response_description="***",
)
@handle_file_data_fetching_failed
async def show_***_file(
    document: DocumentSchema = Depends(DocumentSchemaAsForm),
    text: ***TextSchema = Depends(***TextSchemaAsForm),
    template_type: TemplateTypeSchema = Depends(TemplateTypeSchemaAsForm),
) -> FileResponse:
    return Single***Service(document, text, option, template_type).make()

改善後

ファイルの中身を読み込んでから、HTMLのコンテンツとして返す。

# application service
import base64

import aiofiles
from fastapi.responses import HTMLResponse


async def file_to_html_response(file_path: str) -> HTMLResponse:
    async with aiofiles.open(file_path, "r") as f:
        content = await f.read()
    encoded_content = base64.b64encode(content.encode()).decode()
    svg_data_url = f"data:image/svg+xml;base64,{encoded_content}"
    html_content = f"<img src='{svg_data_url}' alt='SVG Image' />"
    return HTMLResponse(content=html_content)
# routers
@router.post(
    f"***",
    response_class=HTMLResponse,
    response_description="***",
)
@handle_file_data_fetching_failed
async def show_***_file(
    document: DocumentSchema = Depends(DocumentSchemaAsForm),
    text: ***TextSchema = Depends(***TextSchemaAsForm),
    template_type: TemplateTypeSchema = Depends(TemplateTypeSchemaAsForm),
) -> HTMLResponse:
    file = return Single***Service(document, text, option, template_type).make()
    return await file_to_html_response(file.path)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?