ホームページ >バックエンド開発 >Python チュートリアル >FastAPI POST リクエストで JSON とファイルのアップロードの両方を受け入れるにはどうすればよいですか?

FastAPI POST リクエストで JSON とファイルのアップロードの両方を受け入れるにはどうすればよいですか?

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-12-19 10:35:12482ブラウズ

How to accept both JSON and file uploads in a FastAPI POST request?

FastAPI POST リクエストにファイルと JSON 本文の両方を追加するにはどうすればよいですか?

具体的には、以下の例を機能させたいと考えています:

from typing import List
from pydantic import BaseModel
from fastapi import FastAPI, UploadFile, File


app = FastAPI()


class DataConfiguration(BaseModel):
    textColumnNames: List[str]
    idColumn: str


@app.post("/data")
async def data(dataConfiguration: DataConfiguration,
               csvFile: UploadFile = File(...)):
    pass
    # read requested id and text columns from csvFile

これが POST リクエストの適切な方法ではない場合は、アップロードされた CSV ファイルから必要な列を選択する方法を教えてください。 FastAPI.

FastAPI ドキュメントによると:

パス操作で複数の Form パラメータを宣言できますが、JSON として受け取ることが期待される Body フィールドも宣言することはできません。リクエストには、application/json ではなく application/x-www-form-urlencoded を使用してエンコードされた本文が含まれるため (フォームにファイルが含まれている場合、次のようにエンコードされます)

これは FastAPI の制限ではなく、HTTP プロトコルの一部です。

最初に python-multipart をインストールする必要があることに注意してください。アップロードされたファイルは「フォーム データ」として送信されるため、まだ行っていません。例:

pip install python-multipart

以下の例では、エンドポイントは通常の def で定義されていますが、(ニーズに応じて) async def を使用することもできることにも注意してください。 FastAPI の def と async def の詳細については、この回答をご覧ください。

両方のファイルと辞書/JSON データのリストをアップロードする方法をお探しの場合は、お問い合わせください。この回答、および実際の例については、この回答とこの回答 (主に次のメソッドの一部に基づいています) をご覧ください。

メソッド1

ここで説明されているように、File と Form を使用してファイルとフォーム フィールドを同時に定義できます。以下は実際の例です。多数のパラメーターがあり、それらをエンドポイントとは別に定義したい場合は、代わりに依存関係クラスまたは Pydantic モデルを使用して、デカールするフォーム フィールドを宣言する方法に関するこの回答を参照してください。

app.py

from fastapi import Form, File, UploadFile, Request, FastAPI
from typing import List
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/submit")
def submit(
    name: str = Form(...),
    point: float = Form(...),
    is_accepted: bool = Form(...),
    files: List[UploadFile] = File(...),
):
    return {
        "JSON Payload": {
            "name": name,
            "point": point,
            "is_accepted": is_accepted,
        },
        "Filenames": [file.filename for file in files],
    }


@app.get("/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

以下のテンプレートにアクセスして、上記の例をテストできます。 http://127.0.0.1:8000。テンプレートに Jinja コードが含まれていない場合は、単純な HTMLResponse を返すこともできます。

templates/index.html

<!DOCTYPE html>
<html>
   <body>
      <form method="post" action="http://127.0.0.1:8000/submit"  enctype="multipart/form-data">
         name : <input type="text" name="name" value="foo"><br>
         point : <input type="text" name="point" value=0.134><br>
         is_accepted : <input type="text" name="is_accepted" value=True><br>    
         <label for="files">Choose file(s) to upload</label>
         <input type="file">

この例をテストすることもできます。 /docs にあるインタラクティブな OpenAPI/Swagger UI autodoc を使用します。例: http://127.0.0.1:8000/docs、または以下に示すように Python リクエストを使用します:

test.py

from typing import List
from pydantic import BaseModel
from fastapi import FastAPI, UploadFile, File


app = FastAPI()


class DataConfiguration(BaseModel):
    textColumnNames: List[str]
    idColumn: str


@app.post("/data")
async def data(dataConfiguration: DataConfiguration,
               csvFile: UploadFile = File(...)):
    pass
    # read requested id and text columns from csvFile

方法 2

Pydantic モデルと依存関係を使用して、パラメーター化された変数 Base が Base クラスに依存していることを /submit エンドポイント (以下の例) に通知することもできます。このメソッドは、ベース データをクエリ (本体ではなく) パラメータとして期待し、その後 Pydantic モデル (この場合はベース モデル) に対して検証され、変換されることに注意してください。また、重大なセキュリティ リスクが生じるため、クエリ文字列を通じて機密データを決して渡さないようにしてください。このトピックの詳細については、この回答をご覧ください。

Pydantic モデル インスタンスを返すときFastAPI エンドポイント (たとえば、以下の /submit エンドポイント) から (この場合はベース)、で説明されているように、jsonable_encoder を使用して舞台裏で自動的に JSON 文字列に変換されます。この回答の詳細。ただし、モデルをエンドポイント内で独自に JSON 文字列に変換したい場合は、Pydantic の model_dump_json() (Pydantic V2 の場合) (base.model_dump_json() など) を使用して、カスタム レスポンスを直接返すことができます。先ほどのリンクされた回答で説明したとおり。したがって、jsonable_encoder の使用を避けます。それ以外の場合、モデルを自分で辞書に変換するには、Pydantic の model_dump() (Pydantic V2 の場合)、たとえば、base.model_dump() を使用するか、単に dict(base) を使用することもできます (上記のリンクされた回答で説明されているように、エンドポイントでは、FastAPI は引き続き舞台裏で jsonable_encoder を使用します)。関連する Pydantic メソッドとドキュメントについては、この回答も参照してください。

クエリ パラメーターに Pydantic モデルを使用することとは別に、この回答で示されているように、エンドポイントでクエリ パラメーターを直接定義することもできます。 、この回答とこの回答と同様に、

基本的なクエリパラメータに加えて、次の /submit エンドポイントは multipart/form-data としてエンコードされたファイルも期待します。リクエストの本文。

app.py

pip install python-multipart

ここでも、以下のテンプレートを使用してテストできます。今回は JavaScript を使用してアクションを変更します。 form 要素の属性を使用して、フォーム データをクエリ パラメータとして URL の代わりに渡します。 form-data.

templates/index.html

from fastapi import Form, File, UploadFile, Request, FastAPI
from typing import List
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")


@app.post("/submit")
def submit(
    name: str = Form(...),
    point: float = Form(...),
    is_accepted: bool = Form(...),
    files: List[UploadFile] = File(...),
):
    return {
        "JSON Payload": {
            "name": name,
            "point": point,
            "is_accepted": is_accepted,
        },
        "Filenames": [file.filename for file in files],
    }


@app.get("/", response_class=HTMLResponse)
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

以上がFastAPI POST リクエストで JSON とファイルのアップロードの両方を受け入れるにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。