ミドルウェア (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 属性でビューを呼び出し、テンプレートレスポンス例外 を適用します。

ミドルウェアは同期 Python のみ(デフォルト)、非同期 Python のみ、または両方をサポートできます。どのようなリクエストをサポートするのか、またどのようなリクエストを受け取るのかについての詳細は 非同期サポート を参照してください。

ミドルウェアは、あなたの Ptyhon パスのどこでも使うことができます。

__init__(get_response)

ミドルウェアファクトリは get_response 引数を受け取る必要があります。ミドルウェアに対してグローバルな宣言を初期化することもできます。いくつかの注意事項があります:

  • Django は get_response 引数でミドルウェアを初期化するため、__init__() で他の引数を必須にすることはできません。

  • リクエストごとに 1 回呼ばれる __call__() メソッドとは異なり、__init__() はウェブサーバが起動したときに 1回だけ 呼ばれます。

ミドルウェアを不使用としてマークする

起動時にミドルウェアを使うかどうかを決めることは、ときに有用です。この場合、ミドルウェアの __init__() メソッドが MiddlewareNotUsed を投げる可能性があります。Django はミドルウェアプロセスからこのミドルウェアを削除し、DEBUGTrue のときはデバッグメッセージを 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)

requestHttpRequest クラスのオブジェクトです。view_func は、直後に Django が使用する Python の関数です (関数の名前の文字列ではなく、実際の関数オブジェクトです)。view_args はビューに渡される位置引数のリスト、view_kwargs はビューに渡されるキーワード引数のディクショナリです。view_argsview_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)

requestHttpRequest オブジェクトです。exception は、ビュー関数から投げられた Exception オブジェクトです。

Django は、ビューが例外を起こした時に process_exception() を呼び出します。process_exception()None あるいは HttpResponse オブジェクトを返さなければなりません。 HttpResponse オブジェクトを返した場合、テンプレートレスポンスミドルウェアおよびレスポンスミドルウェアが適用され、その結果得られたレスポンスがブラウザに返されます。そうでなければ、 デフォルトの例外のハンドリング が行われます。

繰り返しますが、ミドルウェアは process_exception を含むレスポンスフェーズで逆順に実行されます。例外ミドルウェアがレスポンスを返した場合、そのミドルウェアの上のミドルウェアクラスの process_exception メソッドは全く呼び出されません。

process_template_response()

process_template_response(request, response)

requestHttpRequest クラスのオブジェクトです。response は、Django のビューまたはミドルウェアから返される、TemplateResponse クラスのオブジェクト (あるいはそれと同等なもの) です。

process_template_response() は、レスポンスオブジェクトが render() メソッドを持っている場合、つまり、TemplateResponse クラスのオブジェクトあるいはそれと同等のものである場合に、ビューの実行の直後に呼ばれます。

このメソッドは、render メソッドを実装したレスポンスオブジェクトでなければなりません。このメソッド内では、与えられた response に対して response.template_nameresponse.context_data を修正したり、あるいは、新規に TemplateResponse クラスのオブジェクト (あるいはそれと同等なもの) を作成したりすることができます。

ただし、レスポンスを自分でレンダリング (render) する必要はありません。なぜなら、レスポンスオブジェクトは、すべてのテンプレートレスポンスミドルウェアが呼び出された後に、自動的にレンダリングされるからです。

レスポンスフェーズでは、ミドルウェアは逆順に呼び出されます。これには、process_template_response() も含まれます。

ストリーミングレスポンス (streaming responses) を扱う

HttpResponse とは違い、StreamingHttpResponsecontent 属性を持ちません。そのため、ミドルウェアはもはや、すべてのレスポンスが 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 確認してください。

例外のハンドリング

Django はビューまたはミドルウェアによって起こされた例外を、エラーステータスコードを持つ適切な HTTP レスポンスに自動的に変換します。特定の例外 は 4xx ステータスコードに変換されますが、未知の例外は 500 ステータスコードに変換されます。

この変換は各ミドルウェアの前と後で行われます (玉ねぎの各層の間にある薄い皮のようなものとして考えられます)。そのため、すべてのミドルウェアは常に、呼び出し可能な get_response の呼び出しから返ってきたある種の HTTP レスポンスを取得することに依存しています。ミドルウェアは、get_response への呼び出しを try/except の中にラッピングしたり、後ろのミドルウェアまたはビューから発生する可能性のある例外を心配したり必要はありません。たとえ呼び出しチェーンのすぐ次のミドルウェアがたとえば Http404 例外を発生させたとしても、ミドルウェアがその例外を見ることはなく、代わりに得るのは 404 の status_code を持つ HttpResponse オブジェクトです。

この変換をスキップして例外を上に伝搬するには、DEBUG_PROPAGATE_EXCEPTIONSTrue に設定できます。

非同期サポート

ミドルウェアは同期リクエストと非同期リクエストのどのような組み合わせでもサポートできます。ミドルウェアが両方をサポートできない場合、Django はリクエストをミドルウェアの要求に合うように適応させますが、パフォーマンス上は不利になります。

デフォルトでは、Django はミドルウェアが同期リクエストのみを処理できると想定します。この前提を変更するには、ミドルウェア ファクトリ関数またはクラスに次の属性を設定します。

  • sync_capable は、ミドルウェアが同期リクエストを処理できるかどうかを表すブール値です。デフォルトは True

  • async_capable は、ミドルウェアが非同期リクエストを処理できるかどうかを表すブール値です。デフォルトは False

ミドルウェアが sync_capable = Trueasync_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_viewprocess_template_responseprocess_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__() メソッド:

  1. (定義されている場合) self.process_request(request) を呼び出します。

  2. self.get_response(request) を呼び出し、後のミドルウェアとビューからレスポンスを得ます。

  3. (定義されている場合) self.process_response(request, response) を呼び出します。

  4. レスポンスを返します。

MIDDLEWARE_CLASSES で使われている場合は、__call__() メソッドは決して使われません; Django は process_request()process_response() を直接呼び出します。

ほとんどの場合、この mixin を継承することで、旧式のミドルウェアと十分な下位互換性を持つ新しいシステムと互換性を持たせることができます。 新しい短絡セマンティクスは無害であり、既存のミドルウェアにとっても有益です。 いくつかのケースでは、ミドルウェアクラスは新しいセマンティクスに調整するためにいくつかの変更を必要とすることがあります。

MIDDLEWAREMIDDLEWARE_CLASSES の動作には以下のような違いがあります。

  1. MIDDLEWARE_CLASSES のもとでは、全てのミドルウェアは常にその process_response メソッドが呼び出されます。たとえその前のミドルウェアが process_request メソッドからレスポンスを返してショート(短絡)した場合でもです。 MIDDLEWARE のもとでは、ミドルウェアはオニオンのように動作します。レスポンスが外に出るときに通過するレイヤーは、リクエストが入るときに見たのと同じレイヤーです。ミドルウェアがショートした場合、そのミドルウェアとそれより前にある MIDDLEWARE のミドルウェアのみがレスポンスを見ることになります。

  2. MIDDLEWARE_CLASSES のもとでは、ミドルウェアの process_request メソッドから発生した例外に process_exception が適用されます。 MIDDLEWARE のもとでは、 process_exception はビュー(または TemplateResponserender メソッド)から発生した例外にのみ適用されます。ミドルウェアから発生した例外は適切な HTTP レスポンスに変換され、次のミドルウェアに渡されます。

  3. MIDDLEWARE_CLASSES のもとでは、もし process_response メソッドが例外を発生させた場合、それ以前の全てのミドルウェアの process_response メソッドはスキップされ、常に 500 Internal Server Error HTTP レスポンスが返されます(たとえ発生した例外がたとえば Http404 だったとしてもです)。 MIDDLEWARE のもとでは、ミドルウェアから例外が発生した場合、直ちに適切な HTTP レスポンスに変換され、次のミドルウェアがそのレスポンスを見ることになります。ミドルウェアが例外を発生させたことを理由にミドルウェアがスキップされることはありません。

Back to Top