リクエストコンテキスト
リクエストコンテキストは、リクエスト中にリクエストレベルのデータを追跡します。 リクエスト中に実行される各関数にリクエストオブジェクトを渡すのではなく、代わりに request および session プロキシにアクセスします。
これは、アプリケーションコンテキストに似ており、リクエストとは無関係にアプリケーションレベルのデータを追跡します。 リクエストコンテキストがプッシュされると、対応するアプリケーションコンテキストがプッシュされます。
コンテキストの目的
Flask
アプリケーションがリクエストを処理するとき、WSGIサーバーから受信した環境に基づいてRequest
オブジェクトを作成します。 ワーカー(サーバーに応じてスレッド、プロセス、またはコルーチン)は一度に1つのリクエストのみを処理するため、リクエストデータはそのリクエスト中にそのワーカーに対してグローバルであると見なすことができます。 Flaskは、これに context local という用語を使用します。
リクエストを処理するときに、Flaskはリクエストコンテキストを自動的にプッシュします。 ビュー関数、エラーハンドラー、および要求中に実行されるその他の関数は、現在の要求の要求オブジェクトを指す request プロキシにアクセスできます。
コンテキストの存続期間
Flaskアプリケーションがリクエストの処理を開始すると、リクエストコンテキストがプッシュされ、アプリケーションコンテキストもプッシュされます。 リクエストが終了すると、リクエストコンテキストがポップされ、次にアプリケーションコンテキストがポップされます。
コンテキストは、各スレッド(または他のワーカータイプ)に固有です。 request を別のスレッドに渡すことはできません。他のスレッドは異なるコンテキストスタックを持ち、親スレッドが指しているリクエストを認識しません。
コンテキストローカルはWerkzeugに実装されています。 これが内部でどのように機能するかについての詳細は、 werkzeug:local を参照してください。
コンテキストを手動でプッシュする
リクエストコンテキスト外でリクエストまたはそれを使用するものにアクセスしようとすると、次のエラーメッセージが表示されます。
これは通常、アクティブなリクエストを期待するコードをテストする場合にのみ発生します。 1つのオプションは、test client
を使用して完全なリクエストをシミュレートすることです。 または、with
ブロックでtest_request_context()
を使用すると、ブロックで実行されるすべてのものが、テストデータが入力されたリクエストにアクセスできるようになります。
テストに関係のないコードのどこかにそのエラーが表示された場合は、そのコードをビュー関数に移動する必要があることを示している可能性があります。
インタラクティブなPythonシェルからリクエストコンテキストを使用する方法については、シェルの操作を参照してください。
コンテキストのしくみ
Flask.wsgi_app()
メソッドは、各要求を処理するために呼び出されます。 リクエスト中のコンテキストを管理します。 内部的には、リクエストとアプリケーションのコンテキストはスタック _request_ctx_stack と _app_ctx_stack として機能します。 コンテキストがスタックにプッシュされると、それらに依存するプロキシが使用可能になり、スタックの最上位のコンテキストからの情報を指し示します。
リクエストが開始されると、RequestContext
が作成されてプッシュされます。そのアプリケーションのコンテキストがまだトップコンテキストでない場合は、最初にAppContext
が作成されてプッシュされます。 これらのコンテキストがプッシュされている間、 current_app 、 g 、 request 、および session プロキシは、リクエストを処理する元のスレッドで使用できます。
コンテキストはスタックであるため、リクエスト中にプロキシを変更するために他のコンテキストがプッシュされる場合があります。 これは一般的なパターンではありませんが、高度なアプリケーションで使用して、たとえば、内部リダイレクトを実行したり、さまざまなアプリケーションをチェーンしたりできます。
要求がディスパッチされ、応答が生成されて送信された後、要求コンテキストがポップされ、次にアプリケーションコンテキストがポップされます。 ポップされる直前に、teardown_request()
およびteardown_appcontext()
関数が実行されます。 これらは、ディスパッチ中に未処理の例外が発生した場合でも実行されます。
コールバックとエラー
Flaskは、要求、応答、およびエラーの処理方法に影響を与える可能性のある複数の段階で要求をディスパッチします。 コンテキストは、これらすべての段階でアクティブになります。
Blueprint
は、ブループリントに固有のこれらのイベントのハンドラーを追加できます。 ブループリントがリクエストに一致するルートを所有している場合、ブループリントのハンドラーが実行されます。
- 各リクエストの前に、
before_request()
関数が呼び出されます。 これらの関数の1つが値を返す場合、他の関数はスキップされます。 戻り値は応答として扱われ、view関数は呼び出されません。 before_request()
関数が応答を返さなかった場合、一致したルートのビュー関数が呼び出され、応答が返されます。- ビューの戻り値は実際の応答オブジェクトに変換され、
after_request()
関数に渡されます。 各関数は、変更された、または新しい応答オブジェクトを返します。 - 応答が返された後、コンテキストがポップされ、
teardown_request()
およびteardown_appcontext()
関数が呼び出されます。 これらの関数は、上記のいずれかの時点で未処理の例外が発生した場合でも呼び出されます。
ティアダウン関数の前に例外が発生した場合、Flaskはそれをerrorhandler()
関数と照合して例外を処理し、応答を返します。 エラーハンドラーが見つからない場合、またはハンドラー自体が例外を発生させた場合、Flaskは一般的な500 Internal Server Error
応答を返します。 ティアダウン関数は引き続き呼び出され、例外オブジェクトが渡されます。
デバッグモードが有効になっている場合、未処理の例外は500
応答に変換されず、代わりにWSGIサーバーに伝播されます。 これにより、開発サーバーはインタラクティブデバッガーにトレースバックを提示できます。
分解コールバック
ティアダウンコールバックは、リクエストディスパッチとは独立しており、ポップされたときにコンテキストによって呼び出されます。 ディスパッチ中に未処理の例外が発生した場合でも、手動でプッシュされたコンテキストの場合でも、関数が呼び出されます。 これは、リクエストディスパッチの他の部分が最初に実行されたという保証がないことを意味します。 これらの関数は、他のコールバックに依存せず、失敗しない方法で作成してください。
テスト中は、リクエストの終了後にコンテキストのポップを延期して、テスト関数でコンテキストのデータにアクセスできるようにすることが役立つ場合があります。 test_client()
をwith
ブロックとして使用して、withブロックが終了するまでコンテキストを保持します。
信号
signal_available がtrueの場合、次のシグナルが送信されます。
- request_started は、
before_request()
関数が呼び出される前に送信されます。 - request_finished は、
after_request()
関数が呼び出された後に送信されます。 - got_request_exception は、例外の処理が開始されたとき、ただし
errorhandler()
が検索または呼び出される前に送信されます。 - request_tearing_down は、
teardown_request()
関数が呼び出された後に送信されます。
エラー時のコンテキスト保存
リクエストの最後に、リクエストコンテキストがポップされ、それに関連付けられているすべてのデータが破棄されます。 開発中にエラーが発生した場合は、デバッグのためにデータの破棄を遅らせると便利です。
開発サーバーが開発モードで実行されている場合(FLASK_ENV
環境変数が'development'
に設定されている場合)、エラーとデータは保持され、対話型デバッガーに表示されます。
この動作は、 PRESERVE_CONTEXT_ON_EXCEPTION 構成で制御できます。 上記のように、開発環境ではデフォルトでTrue
になります。
PRESERVE_CONTEXT_ON_EXCEPTION を本番環境で有効にしないでください。例外により、アプリケーションがメモリをリークする原因になります。
プロキシに関する注意
Flaskによって提供されるオブジェクトの一部は、他のオブジェクトのプロキシです。 プロキシは各ワーカースレッドで同じ方法でアクセスされますが、このページで説明されているように、舞台裏で各ワーカーにバインドされている一意のオブジェクトを指します。
ほとんどの場合、それを気にする必要はありませんが、このオブジェクトが実際のプロキシであることを知っておくとよい例外がいくつかあります。
- プロキシオブジェクトは、実際のオブジェクトタイプとしてそのタイプを偽造することはできません。 インスタンスチェックを実行する場合は、プロキシされるオブジェクトに対して実行する必要があります。
- Signals の送信やバックグラウンドスレッドへのデータの受け渡しなど、特定のオブジェクト参照が重要な場合。
プロキシされる基になるオブジェクトにアクセスする必要がある場合は、_get_current_object()
メソッドを使用します。