Trailing Slashのリダイレクト動作の制御
FastAPIを含む多くのフレームワークで、末尾にスラッシュのある(Trailing Slash)リクエストを受け取ったときにマッチするurlが見つからないと、 自動で末尾にスラッシュの無いurlにリダイレクトします。
しかし、APIの開発においては、エンドポイントは正確に指定すべきだ、という考え方もあり、
今回はその考え方に則り、
FastAPIのバージョン0.98.0で有効になったredirect_slashes=False
オプションを使って、自動リダイレクトを無効に設定してみます。
自動リダイレクトを無効にすることで、正確にエンドポイントを指定しないと、404エラーを出すようにすることが狙いです。
FastAPIのバージョン0.98.0のインストール
まず、FastAPIのバージョン0.98.0をインストールします
$ pip install fastapi==0.98.0
インストール後、pip list
でバージョンを確認します
$ pip list Package Version ----------------- ------------ (略) fastapi 0.98.0 (略)
インストールできました。 次は、エンドポイントを設定します。
末尾にスラッシュのないエンドポイントの設定
末尾にスラッシュのないエンドポイントを用意します
@app.get("/hello") async def say_hello(): return {"message": "Hello!"}
アプリケーションを起動します
$ uvicorn main:app --reload
Trailing Slashのリダイレクト動作のテスト
"http://localhost:8000/hello"を叩いてみます
INFO: 127.0.0.1:65240 - "GET /hello HTTP/1.1" 200 OK
次に末尾にスラッシュがある "http://localhost:8000/hello/"を叩いてみると、
INFO: 127.0.0.1:65261 - "GET /hello/ HTTP/1.1" 307 Temporary Redirect INFO: 127.0.0.1:65261 - "GET /hello HTTP/1.1" 200 OK
自動でリダイレクトがかかっていることが分かります。
FastAPIはredirect_slashes=True
がデフォルト設定になっているからです。
redirect_slashesオプションの設定変更
Trailing Slashの自動リダイレクトを無効にするには、
FastAPIのインスタンスを作成する際に、redirect_slashes=False
にします。
app = FastAPI(redirect_slashes=False) @app.get("/hello") async def say_hello(): return {"message": "Hello!"}
これで "http://localhost:8000/hello/"を叩いてみると、
INFO: 127.0.0.1:65394 - "GET /hello/ HTTP/1.1" 404 Not Found
になります。 これで、開発段階でも正確にエンドポイントを叩くことを強制することができます。
最後に、FastAPIがどのようにredirect_slashesオプションを実装しているのか確認してみます。
redirect_slashesオプションの実装を確認する
FastAPIのソースコードを見ると、applications.pyファイル内でredirect_slashesオプションが定義されています。
FastAPIのアプリケーションインスタンスが生成される際、redirect_slashesオプションはコンストラクタ引数として与えられ、FastAPIの内部でStarletteのルーターオブジェクトを作成するときに、このオプションがルーターのコンストラクタに渡され実際のリダイレクト動作を制御しているようです。
Starletteには、2019年にはredirect_slashes
オプションが導入されており、FastAPIのバージョン0.98.0でFastAPIから制御できるようになったようです。 Version 0.13 by tomchristie · Pull Request #704 · encode/starlette · GitHub
FastAPI
# fastapi/applications.py class FastAPI(Starlette): def __init__( self: AppType, redirect_slashes: bool = True, redirect_slashes=redirect_slashes # (略) self.router: routing.APIRouter = routing.APIRouter( routes=routes, redirect_slashes=redirect_slashes,
Starlette
# starlette/routing.py class Router: def __init__( self, ... ) -> None: self.redirect_slashes = redirect_slashes
参考
- APIRouter class - FastAPI
- Release Notes - FastAPI
- ✨ Add allow disabling `redirect_slashes` at the FastAPI app level by cyberlis · Pull Request #3432 · tiangolo/fastapi · GitHub
- Version 0.13 by tomchristie · Pull Request #704 · encode/starlette · GitHub