ミドルウェア (Middleware)¶
ミドルウェアは、Django のリクエスト/レスポンス処理にフックを加えるためのフレームワークです。これは、Django の入力あるいは出力をグローバルに置き換えるための、軽量で低レベルの「プラグイン」システムです。
各ミドルウェアのコンポーネントは、それぞれある特定の機能を実行する役目を持っています。たとえば、Django には AuthenticationMiddleware
というミドルウェアコンポーネントがあります。このミドルウェアは、セッションを利用して、リクエストとユーザーとを関連付けます。
このドキュメントでは、ミドルウェアが機能するしくみ、ミドルウェアを有効にする方法、そして、自分でミドルウェアを書く方法について説明します。Django には組み込みのミドルウェアがいくつか用意されているので、自分で書かなくてもすぐに使い始めることができます。これらの組み込みのミドルウェアについては、組み込みミドルウェアリファレンス にドキュメントされているので参照してください。
自分でミドルウェアを書く¶
ミドルウェアファクトリは、ミドルウェアを返す get_response
を取る呼び出し可能なオブジェクトです。ミドルウェアは (ビューと同じように) リクエストを受け取ってレスポンスを返す呼び出し可能なオブジェクトです。
ミドルウェアは、以下のように関数として書くことができます:
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
もしくは、インスタンスを呼び出し可能なクラスとして書くこともできます:
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Django によって渡される get_response
呼び出し可能オブジェクトは、実際の (これがミドルウェアの最後にリストアップされている場合) ビューか、チェーン内の次のミドルウェアの可能性があります。現在のミドルウェアはこれが何かを知る必要も気にする必要もなく、ただ次に来るものを表します。
上記は、わずかに単純化したものです -- チェーン内の際にリストアップされたミドルウェアに対する get_response
は実際のビューではなく ビューミドルウェア を適用する処理を行うハンドラからのラッパーメソッドです。これは適切な URL 属性でビューを呼び出し、テンプレートレスポンス と 例外 を適用します。
Middleware can either support only synchronous Python (the default), only asynchronous Python, or both. See 非同期サポート for details of how to advertise what you support, and know what kind of request you are getting.
ミドルウェアは、あなたの Ptyhon パスのどこでも使うことができます。
__init__(get_response)
¶
ミドルウェアファクトリは get_response
引数を受け取る必要があります。ミドルウェアに対してグローバルな宣言を初期化することもできます。いくつかの注意事項があります:
- Django は
get_response
引数でミドルウェアを初期化するため、__init__()
で他の引数を必須にすることはできません。 - Unlike the
__call__()
method which is called once per request,__init__()
is called only once, when the web server starts.
ミドルウェアを不使用としてマークする¶
起動時にミドルウェアを使うかどうかを決めることは、ときに有用です。この場合、ミドルウェアの __init__()
メソッドが MiddlewareNotUsed
を投げる可能性があります。Django はミドルウェアプロセスからこのミドルウェアを削除し、DEBUG
が True
のときはデバッグメッセージを django.request ロガーに記録します。
ミドルウェアを有効にする¶
ミドルウェア要素をアクティブ化するには、Django の設定内の MIDDLEWARE
リストに追加してください。
MIDDLEWARE
では、各ミドルウェア要素は文字列で表されます: ミドルウェアファクトリのクラスや関数名に対する完全な Python パスです。 例えば、以下は django-admin startproject
で生成されるデフォルト値です:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Django の導入では、ミドルウェアは必須ではありません — お望みならば MIDDLEWARE
は空でも構いません — しかし、最低でも CommonMiddleware
を使うことを強くお勧めします。
ミドルウェアは他のミドルウェアに依存するため、MIDDLEWARE
の順番は重要です。AuthenticationMiddleware
はセッション内に認証済みユーザを保持します。したがって、SessionMiddleware
の後に起動する必要があります。Django のミドルウェアクラスの順番については、Middleware の順序 を参照してください。
ミドルウェアの順番とレイヤ¶
ビューを呼び出す前、リクエストの段階で、Django は MIDDLEWARE
内で定義された順番で上から下に向かってミドルウェアを適用します。
これはタマネギのように考えることができます: 各ミドルウェアクラスは、タマネギの中心にあるビューをラップする "レイヤ" です。リクエストがタマネギのすべてのレイヤ (つまり中心にあるビューにいたる全ての道) を通過すると (各レイヤーは get_response
を呼び出してリクエストを次のレイヤに渡します)、レスポンスは各レイヤーを (逆順で) 通過して戻ります。
いずれかのレイヤが短絡し、get_response
を呼び出さずにレスポンスを返すことを決定した場合、そのレイヤの内側にあるタマネギの (ビューを含む) レイヤはリクエストおよびレスポンスを受け取りません。 レスポンスは、リクエストが通過したのと同じレイヤーを介してのみ返されます。
他のミドルウェアのフック¶
上述した基本的なリクエストやレスポンスのミドルウェアパターンのほかに、3 つの特殊なメソッドをクラスベースのミドルウェアに追加することができます:
process_view()
¶
-
process_view
(request, view_func, view_args, view_kwargs)¶
request
は HttpRequest
クラスのオブジェクトです。view_func
は、直後に Django が使用する Python の関数です (関数の名前の文字列ではなく、実際の関数オブジェクトです)。view_args
はビューに渡される位置引数のリスト、view_kwargs
はビューに渡されるキーワード引数のディクショナリです。view_args
も view_kwargs
も、第一引数 (request
) を含んでいません。
process_view()
は、Django がビューを呼び出す直前に呼び出されます。
None
もしくは HttpResponse
オブジェクトを返す必要があります。None
を返す場合、Django はこのリクエストの処理を続け、他のすべての process_view()
ミドルウェアを実行し、さらに適切なビューを実行します。HttpResponse
オブジェクトを返す場合、Django はわざわざ適切なビューを呼び出すことはしません; レスポンスミドルウェアを HttpResponse
に適用し、結果を返します。
注釈
ビューが実行される、ミドルウェアの内側もしくは process_view()
内の request.POST
にアクセスすると、ミドルウェアの後に実行される全てのビューが リクエストに対するアップロードハンドラを修正する ことができなくなるため、通常は避けられるべきです。
CsrfViewMiddleware
クラスだけは例外と考えていいでしょう。というのも、このミドルウェアには、csrf_exempt()
と csrf_protect()
というデコレータが用意されていて、このデコレータを使えば、CSRF の検証が必要になったどの時点でも、明示的にビューを制御できるからです。
process_exception()
¶
-
process_exception
(request, exception)¶
request
は HttpRequest
オブジェクトです。exception
は、ビュー関数から投げられた Exception
オブジェクトです。
Django calls process_exception()
when a view raises an exception.
process_exception()
should return either None
or an
HttpResponse
object. If it returns an
HttpResponse
object, the template response and response
middleware will be applied and the resulting response returned to the
browser. Otherwise, default exception handling kicks in.
Again, middleware are run in reverse order during the response phase, which
includes process_exception
. If an exception middleware returns a response,
the process_exception
methods of the middleware classes above that
middleware won't be called at all.
process_template_response()
¶
-
process_template_response
(request, response)¶
request
は HttpRequest
クラスのオブジェクトです。response
は、Django のビューまたはミドルウェアから返される、TemplateResponse
クラスのオブジェクト (あるいはそれと同等なもの) です。
process_template_response()
は、レスポンスオブジェクトが render()
メソッドを持っている場合、つまり、TemplateResponse
クラスのオブジェクトあるいはそれと同等のものである場合に、ビューの実行の直後に呼ばれます。
このメソッドは、render
メソッドを実装したレスポンスオブジェクトでなければなりません。このメソッド内では、与えられた response
に対して response.template_name
や response.context_data
を修正したり、あるいは、新規に TemplateResponse
クラスのオブジェクト (あるいはそれと同等なもの) を作成したりすることができます。
ただし、レスポンスを自分でレンダリング (render) する必要はありません。なぜなら、レスポンスオブジェクトは、すべてのテンプレートレスポンスミドルウェアが呼び出された後に、自動的にレンダリングされるからです。
レスポンスフェーズでは、ミドルウェアは逆順に呼び出されます。これには、process_template_response()
も含まれます。
ストリーミングレスポンス (streaming responses) を扱う¶
HttpResponse
とは違い、StreamingHttpResponse
は content
属性を持ちません。そのため、ミドルウェアはもはや、すべてのレスポンスが content
属性を持っていることを前提にすることができなくなります。したがって、content にアクセスする必要がある場合には、レスポンスオブジェクトがストリーミングレスポンスオブジェクトかどうかをチェックし、その結果によって処理を場合分けしなければなりません。
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = alter_content(response.content)
注釈
streaming_content
はメモリ上に置けないくらい大きいと想定しておくべきです。レスポンスミドルウェアでは、これを新しいジェネレータでラッピングすることができます。しかし、このジェネレータを処理することがあってはなりません。典型的なラッピング方法は、次のようになります。
def wrap_streaming_content(content):
for chunk in content:
yield alter_content(chunk)
StreamingHttpResponse
は同期イテレータと非同期イテレータの両方を受け付けます。ラッピング関数は一致する必要があります。ミドルウェアがこの種のイテレータをサポートする必要がある場合には、StreamingHttpResponse.is_async
確認してください。
非同期イテレータを使用したストリーミングレスポンスのサポートが追加されました。
Exception handling¶
Django はビューまたはミドルウェアによって起こされた例外を、エラーステータスコードを持つ適切な HTTP レスポンスに自動的に変換します。特定の例外 は 4xx ステータスコードに変換されますが、未知の例外は 500 ステータスコードに変換されます。
この変換は各ミドルウェアの前と後で行われます (玉ねぎの各層の間にある薄い皮のようなものとして考えられます)。そのため、すべてのミドルウェアは常に、呼び出し可能な get_response
の呼び出しから返ってきたある種の HTTP レスポンスを取得することに依存しています。ミドルウェアは、get_response
への呼び出しを try/except
の中にラッピングしたり、後ろのミドルウェアまたはビューから発生する可能性のある例外を心配したり必要はありません。たとえ呼び出しチェーンのすぐ次のミドルウェアがたとえば Http404
例外を発生させたとしても、ミドルウェアがその例外を見ることはなく、代わりに得るのは 404 の status_code
を持つ HttpResponse
オブジェクトです。
この変換をスキップして例外を上に伝搬するには、DEBUG_PROPAGATE_EXCEPTIONS
を True
に設定できます。
非同期サポート¶
ミドルウェアは同期リクエストと非同期リクエストのどのような組み合わせでもサポートできます。ミドルウェアが両方をサポートできない場合、Django はリクエストをミドルウェアの要求に合うように適応させますが、パフォーマンス上は不利になります。
デフォルトでは、Django はミドルウェアが同期リクエストのみを処理できると想定します。この前提を変更するには、ミドルウェア ファクトリ関数またはクラスに次の属性を設定します。
sync_capable
は、ミドルウェアが同期リクエストを処理できるかどうかを表すブール値です。デフォルトはTrue
。async_capable
は、ミドルウェアが非同期リクエストを処理できるかどうかを表すブール値です。デフォルトはFalse
。
ミドルウェアが sync_capable = True
と async_capable = True
の両方を持っている場合、Django はそれを変換せずにリクエストに渡します。この場合、ミドルウェアが非同期リクエストを受け取ったかどうかは、渡された get_response
オブジェクトがコルーチン関数かどうかを asgiref.sync.iscoroutinefunction
で確認できます。
django.utils.decorators
モジュールには、ミドルウェアのファクトリ関数にこれらのフラグを設定できる sync_only_middleware()
、async_only_middleware()
、sync_and_async_middleware()
デコレータが含まれています。
返された callable は、get_response
メソッドの同期または非同期の性質と一致していなければなりません。非同期の get_response
がある場合、コルーチン関数 (async def
) を返さなければなりません。
process_view
、process_template_response
、process_exception
メソッドは、もし提供された場合、同期/非同期モードにも一致するように適応する必要があります。ただし、適応させなかった場合でも Django は必要に応じてそれらを個別に調整させますが、パフォーマンスがさらに低下します。
以下は、両方をサポートするミドルウェア関数の作成方法の例です。
from asgiref.sync import iscoroutinefunction
from django.utils.decorators import sync_and_async_middleware
@sync_and_async_middleware
def simple_middleware(get_response):
# One-time configuration and initialization goes here.
if iscoroutinefunction(get_response):
async def middleware(request):
# Do something here!
response = await get_response(request)
return response
else:
def middleware(request):
# Do something here!
response = get_response(request)
return response
return middleware
注釈
同期呼び出しと非同期呼び出しの両方をサポートするハイブリッド ミドルウェアを宣言する場合、得られる呼び出しの種類が基になるビューと一致しなくなる可能性があります。Django は同期/非同期の遷移ができる限り少なくなるように、ミドルウェアのコールスタックを最適化します。
よって、たとえ非同期ビューをラップしている場合でも、ビューとの間に他の同期ミドルウェアが存在する場合、同期モードで呼び出される可能性があります。
非同期のクラスベースのミドルウェアを使用する場合は、次のようにしてインスタンスが正しくコルーチン関数としてマークされていることを確認する必要があります。
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
class AsyncMiddleware:
async_capable = True
sync_capable = False
def __init__(self, get_response):
self.get_response = get_response
if iscoroutinefunction(self.get_response):
markcoroutinefunction(self)
async def __call__(self, request):
response = await self.get_response(request)
# Some logic ...
return response
Django 1.10 以前のスタイルのミドルウェアをアップグレードする¶
-
class
django.utils.deprecation.
MiddlewareMixin
¶
Django は django.utils.deprecation.MiddlewareMixin
を提供しています。これを使うと、MIDDLEWARE
と古い MIDDLEWARE_CLASSES
両方に互換性があり、同期リクエストと非同期リクエストの両方をサポートするミドルウェアクラスの作成が簡単になります。Django に含まれるすべてのミドルウェアクラスは、両方の設定と互換性があります。
mixin は __init__()
メソッドを提供しています。これは、必須の get_response
引数を受け取って self.get_response
に保持します。
__call__()
メソッド:
- (定義されている場合)
self.process_request(request)
を呼び出します。 self.get_response(request)
を呼び出し、後のミドルウェアとビューからレスポンスを得ます。- (定義されている場合)
self.process_response(request, response)
を呼び出します。 - レスポンスを返します。
MIDDLEWARE_CLASSES
で使われている場合は、__call__()
メソッドは決して使われません; Django は process_request()
と process_response()
を直接呼び出します。
ほとんどの場合、この mixin を継承することで、旧式のミドルウェアと十分な下位互換性を持つ新しいシステムと互換性を持たせることができます。 新しい短絡セマンティクスは無害であり、既存のミドルウェアにとっても有益です。 いくつかのケースでは、ミドルウェアクラスは新しいセマンティクスに調整するためにいくつかの変更を必要とすることがあります。
MIDDLEWARE
と MIDDLEWARE_CLASSES
の動作には以下のような違いがあります。
- Under
MIDDLEWARE_CLASSES
, every middleware will always have itsprocess_response
method called, even if an earlier middleware short-circuited by returning a response from itsprocess_request
method. UnderMIDDLEWARE
, middleware behaves more like an onion: the layers that a response goes through on the way out are the same layers that saw the request on the way in. If a middleware short-circuits, only that middleware and the ones before it inMIDDLEWARE
will see the response. - Under
MIDDLEWARE_CLASSES
,process_exception
is applied to exceptions raised from a middlewareprocess_request
method. UnderMIDDLEWARE
,process_exception
applies only to exceptions raised from the view (or from therender
method of aTemplateResponse
). Exceptions raised from a middleware are converted to the appropriate HTTP response and then passed to the next middleware. - Under
MIDDLEWARE_CLASSES
, if aprocess_response
method raises an exception, theprocess_response
methods of all earlier middleware are skipped and a500 Internal Server Error
HTTP response is always returned (even if the exception raised was e.g. anHttp404
). UnderMIDDLEWARE
, an exception raised from a middleware will immediately be converted to the appropriate HTTP response, and then the next middleware in line will see that response. Middleware are never skipped due to a middleware raising an exception.