コンテンツにスキップ

パス パラメータ

Pythonのフォーマット文字列と同じ構文を使用して、パスの「パラメータ」または「変数」を宣言できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id):
    return {"item_id": item_id}

パス パラメータ item_id の値は、引数 item_id として関数に渡されます。

そのため、この例を実行してhttp://127.0.0.1:8000/items/fooにアクセスすると、次のレスポンスが表示されます。

{"item_id":"foo"}

型付きパス パラメータ

標準のPython型アノテーションを使用して、関数でパス パラメータの型を宣言できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

この場合、item_idint であると宣言されています。

確認

これにより、関数内でエラーチェック、補完などのエディターサポートが得られます。

データ 変換

この例を実行して、ブラウザでhttp://127.0.0.1:8000/items/3を開くと、次のレスポンスが表示されます。

{"item_id":3}

確認

関数が受信(および返却)した値は、文字列 "3" ではなく、Pythonの int としての 3 であることに注意してください。

そのため、この型宣言により、FastAPI は自動的にリクエストの "パース" を行います。

データ検証

しかし、ブラウザでhttp://127.0.0.1:8000/items/fooにアクセスすると、次のHTTPエラーが表示されます。

{
  "detail": [
    {
      "type": "int_parsing",
      "loc": [
        "path",
        "item_id"
      ],
      "msg": "Input should be a valid integer, unable to parse string as an integer",
      "input": "foo",
      "url": "https://errors.pydantic.dev/2.1/v/int_parsing"
    }
  ]
}

これは、パス パラメータ item_id の値が "foo" であり、これは int ではないためです。

http://127.0.0.1:8000/items/4.2のように、int の代わりに float を指定した場合にも同じエラーが発生します。

確認

そのため、同じPython型宣言により、FastAPI はデータ検証を提供します。

エラーは、検証に合格しなかった箇所を正確に示していることにも注意してください。

これは、APIと対話するコードの開発とデバッグを行う際に非常に役立ちます。

ドキュメント

ブラウザでhttp://127.0.0.1:8000/docsを開くと、次のような自動的、インタラクティブなAPIドキュメントが表示されます。

確認

ここでも、同じPython型宣言だけで、FastAPI は自動的、インタラクティブなドキュメント(Swagger UIを統合)を提供します。

パス パラメータは整数として宣言されていることに注意してください。

標準ベースの利点、代替ドキュメント

生成されたスキーマはOpenAPI標準に基づいているため、多くの互換ツールが存在します。

このため、FastAPI自体が代替APIドキュメント(ReDocを使用)を提供しており、http://127.0.0.1:8000/redocからアクセスできます。

同様に、多くの互換ツールが存在します。多くの言語のコード生成ツールを含みます。

Pydantic

すべてのデータ検証はPydanticによって内部的に実行されるため、そのすべての利点が得られます。そして、あなたは信頼できる手に委ねられていることがわかります。

strfloatbool、その他多くの複雑なデータ型で同じ型宣言を使用できます。

これらのいくつかは、チュートリアルの次の章で詳しく説明します。

順序が重要

パス操作を作成する場合、固定パスを持つ状況が発生することがあります。

たとえば、/users/meは現在のユーザーに関するデータを取得するためのパスだとします。

また、ユーザーIDによって特定のユーザーに関するデータを取得するためのパス/users/{user_id}を持つこともできます。

パス操作は順番に評価されるため、/users/meのパスが/users/{user_id}の前に宣言されていることを確認する必要があります。

from fastapi import FastAPI

app = FastAPI()


@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

そうでない場合、/users/{user_id}のパスは/users/meにも一致し、「me」という値を持つパラメータuser_idを受け取っていると「誤解」します。

同様に、パス操作を再定義することはできません。

from fastapi import FastAPI

app = FastAPI()


@app.get("/users")
async def read_users():
    return ["Rick", "Morty"]


@app.get("/users")
async def read_users2():
    return ["Bean", "Elfo"]

パスが最初に一致するため、常に最初のものが使用されます。

事前定義された値

パス パラメータを受け取る*パス操作*があり、有効な*パス パラメータ*の値を事前に定義したい場合は、標準の Python Enum を使用できます。

Enumクラスを作成する

Enumをインポートし、strEnumから継承するサブクラスを作成します。

strから継承することで、APIドキュメントは値がstring型でなければならないことを認識し、正しくレンダリングできるようになります。

次に、固定値を持つクラス属性を作成します。これが利用可能な有効な値になります。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ヒント

「AlexNet」、「ResNet」、「LeNet」は、機械学習モデルの名前です。

*パス パラメータ*を宣言する

次に、作成した列挙型クラス(ModelName)を使用して、型アノテーション付きの*パス パラメータ*を作成します。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ドキュメントを確認する

*パス パラメータ*に使用可能な値が事前に定義されているため、インタラクティブドキュメントはそれらを適切に表示できます。

Python *列挙型*の操作

*パス パラメータ*の値は、*列挙型メンバー*になります。

*列挙型メンバー*を比較する

作成した列挙型ModelNameの*列挙型メンバー*と比較できます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

*列挙値*を取得する

model_name.value、または一般的にはyour_enum_member.valueを使用して、実際の値(この場合はstr)を取得できます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

ヒント

また、ModelName.lenet.valueを使用して値"lenet"にアクセスすることもできます。

*列挙型メンバー*を返す

*パス操作*から*列挙型メンバー*を返すことができます。JSON本文(例:dict)にネストすることもできます。

クライアントに返す前に、対応する値(この場合は文字列)に変換されます。

from enum import Enum

from fastapi import FastAPI


class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


app = FastAPI()


@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}

    return {"model_name": model_name, "message": "Have some residuals"}

クライアントでは、次のようなJSONレスポンスが得られます。

{
  "model_name": "alexnet",
  "message": "Deep Learning FTW!"
}

パスを含むパス パラメータ

パス/files/{file_path}を持つ*パス操作*があるとします。

しかし、file_path自体にhome/johndoe/myfile.txtのような*パス*を含める必要があります。

そのため、そのファイルのURLは/files/home/johndoe/myfile.txtのようになります。

OpenAPIのサポート

OpenAPIは、*パス パラメータ*に*パス*を含めることを宣言する方法をサポートしていません。テストと定義が困難なシナリオにつながる可能性があるためです。

それでも、Starletteの内部ツールの1つを使用して、FastAPIで実行できます。

ドキュメントは引き続き機能しますが、パラメータにパスを含める必要があることを示すドキュメントは追加されません。

パスコンバータ

Starletteのオプションを直接使用すると、次のようなURLを使用して*パス*を含む*パス パラメータ*を宣言できます。

/files/{file_path:path}

この場合、パラメータの名前はfile_pathで、最後の部分の:pathは、パラメータが任意の*パス*と一致する必要があることを示しています。

そのため、次のように使用できます。

from fastapi import FastAPI

app = FastAPI()


@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

ヒント

パラメータに、先頭にスラッシュ(/)が付いた/home/johndoe/myfile.txtを含める必要がある場合があります。

その場合、URLは/files//home/johndoe/myfile.txtになり、fileshomeの間にダブルスラッシュ(//)が付きます。

要約

FastAPIでは、短く、直感的で標準的なPython型宣言を使用することで、次のことが得られます。

  • エディタサポート:エラーチェック、自動補完など
  • データ「パース
  • データ検証
  • APIアノテーションと自動ドキュメント

そして、それらを一度だけ宣言する必要があります。

これはおそらく、(生の性能以外に)FastAPIが代替フレームワークと比較して目に見える主な利点です。