# Django ミドルウェア完全ガイド 2026:カスタムミドルウェア、ロギング実装と面接対策 > Django 6.0のミドルウェアを徹底解説。カスタムミドルウェアの作成方法、非同期ミドルウェア、リクエストIDロギング、そして技術面接で頻出の質問と回答を詳しく説明します。 - Published: 2026-09-18 - Updated: 2026-09-18 - Author: Anthony Fillion-Maillet - Tags: django, middleware, python, logging, interview - Reading time: 12 min --- Djangoミドルウェアは、すべてのリクエスト・レスポンスサイクルの中核を担っています。しかし、多くの開発者はミドルウェアをブラックボックスとして扱いがちです。ミドルウェアの実行順序、カスタムミドルウェアの作成タイミング、ミドルウェアレベルでのロギング実装を理解することは、技術面接においてシニアDjango開発者とジュニア開発者を区別する重要なポイントとなります。 > **Django 6.0 ミドルウェア** > > Django 6.0では、ネイティブのasync/await構文による非同期ミドルウェアが完全にサポートされています。ミドルウェアクラスに`async_capable = True`と`sync_capable = False`を設定することで、スレッドオーバーヘッドなしでリクエストを処理できます。 ## Djangoミドルウェアのリクエスト・レスポンスサイクルにおける実行順序 Djangoのミドルウェアは「オニオン(玉ねぎ)モデル」に従って動作します。リクエストが到着すると、`MIDDLEWARE`リストの上から下へ各ミドルウェアを通過します。ビューがレスポンスを返すと、レスポンスは下から上へミドルウェアを逆順に通過します。この順序は重要であり、セッションデータが必要なミドルウェアは`SessionMiddleware`の後に配置する必要があります。 Django 6.0のデフォルト`MIDDLEWARE`設定は以下の通りです: ```python # settings.py 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", ] ``` 各ミドルウェアは、`get_response()`を呼び出す代わりに`HttpResponse`を直接返すことで、チェーンをショートサーキット(短絡)できます。この場合、内側のミドルウェアとビューは実行されませんが、外側のミドルウェアは引き続きレスポンスを処理します。 ## Django 6.0でのクラスベースカスタムミドルウェアの作成 現代のミドルウェアパターンでは、`__init__`と`__call__`メソッドを持つクラスが必要です。`__init__`メソッドはサーバー起動時に一度だけ実行され、`__call__`はリクエストごとに実行されます。 ```python # middleware/timing.py import time import logging logger = logging.getLogger(__name__) class RequestTimingMiddleware: """Logs the time taken to process each request.""" def __init__(self, get_response): # Called once when the server starts self.get_response = get_response def __call__(self, request): # Code before the view executes start_time = time.perf_counter() # Call the next middleware or view response = self.get_response(request) # Code after the view executes duration_ms = (time.perf_counter() - start_time) * 1000 logger.info( "Request %s %s completed in %.2fms", request.method, request.path, duration_ms ) return response ``` ミドルウェアを`settings.py`に登録するには、インポートパスを`MIDDLEWARE`に追加します。配置位置は重要であり、タイミングミドルウェアはリクエストライフサイクル全体を計測するためにリストの早い位置に配置します。 ## 高並行性Djangoアプリケーション向け非同期ミドルウェア Django 6.0は非同期リクエストをネイティブに処理します。ASGIで非同期ビューを実行するアプリケーションでは、同期ミドルウェアがパフォーマンスボトルネックを引き起こします。これはDjangoが同期ミドルウェアをスレッドプールエグゼキュータでラップするためです。非同期ミドルウェアを作成することで、このオーバーヘッドを排除できます。 ```python # middleware/async_timing.py import time import logging from asgiref.sync import iscoroutinefunction, markcoroutinefunction logger = logging.getLogger(__name__) class AsyncRequestTimingMiddleware: """Async-native timing middleware for ASGI deployments.""" 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): start_time = time.perf_counter() # Await the next middleware or async view response = await self.get_response(request) duration_ms = (time.perf_counter() - start_time) * 1000 logger.info( "Async request %s %s completed in %.2fms", request.method, request.path, duration_ms ) return response ``` `django.utils.decorators`の`@sync_and_async_middleware`デコレータは、ランタイムで`iscoroutinefunction(get_response)`をチェックすることで、同期と非同期の両方のリクエストを処理するミドルウェアを作成します。 ## リクエストIDロギングミドルウェアの実装 分散システム間でログを相関させるには、リクエストごとに一意の識別子が必要です。このパターンは[Django面接質問](/technologies/django/interview-questions/django-middleware)で頻繁に登場します。ミドルウェア、ロギング、本番システムのデバッグに関する理解を示すためです。 ```python # middleware/request_id.py import uuid import logging class RequestIDMiddleware: """Attaches a unique request ID to every request for log correlation.""" def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # Generate or extract request ID from header request_id = request.headers.get("X-Request-ID") if not request_id: request_id = str(uuid.uuid4()) # Attach to request object for access in views request.request_id = request_id response = self.get_response(request) # Add request ID to response headers response["X-Request-ID"] = request_id return response ``` すべてのログメッセージに`request_id`を含めるには、カスタムロギングフィルターを作成します: ```python # logging_filters.py import logging class RequestIDFilter(logging.Filter): """Injects request_id into log records.""" def filter(self, record): from django.middleware.request_id import local record.request_id = getattr(local, "request_id", "no-request-id") return True ``` `settings.py`でフィルターを設定します: ```python # settings.py LOGGING = { "version": 1, "disable_existing_loggers": False, "filters": { "request_id": { "()": "logging_filters.RequestIDFilter", }, }, "formatters": { "verbose": { "format": "[{levelname}] {asctime} [{request_id}] {name}: {message}", "style": "{", }, }, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "verbose", "filters": ["request_id"], }, }, "root": { "handlers": ["console"], "level": "INFO", }, } ``` ## きめ細かな制御のためのミドルウェアフックメソッド `__call__`以外にも、Djangoミドルウェアは3つのフックメソッドをサポートしています:`process_view`、`process_exception`、`process_template_response`。これらはリクエストライフサイクルの特定のポイントで制御を提供します。 ### 実行前ロジック用のprocess_view ```python # middleware/permission_check.py from django.http import HttpResponseForbidden class PermissionCheckMiddleware: """Checks permissions before view execution.""" def __init__(self, get_response): self.get_response = get_response def __call__(self, request): return self.get_response(request) def process_view(self, request, view_func, view_args, view_kwargs): # Access view metadata required_permission = getattr(view_func, "required_permission", None) if required_permission: if not request.user.has_perm(required_permission): return HttpResponseForbidden("Permission denied") # Return None to continue to the view return None ``` このパターンにより、ビューにパーミッション要件をデコレートできます: ```python # views.py def require_permission(perm): def decorator(view_func): view_func.required_permission = perm return view_func return decorator @require_permission("app.can_edit") def edit_resource(request, pk): # View logic pass ``` ### 一元化されたエラーハンドリング用のprocess_exception ```python # middleware/error_tracking.py import logging import traceback logger = logging.getLogger(__name__) class ErrorTrackingMiddleware: """Logs exceptions with full context before Django handles them.""" def __init__(self, get_response): self.get_response = get_response def __call__(self, request): return self.get_response(request) def process_exception(self, request, exception): logger.error( "Unhandled exception in %s %s: %s\n%s", request.method, request.path, str(exception), traceback.format_exc(), extra={ "user_id": getattr(request.user, "id", None), "request_id": getattr(request, "request_id", None), } ) # Return None to let Django handle the exception return None ``` ミドルウェアの`process_exception`メソッドは逆順で実行されます。いずれかが`HttpResponse`を返すと、リストの前方にあるミドルウェアは例外を見ることができません。 ## Django 6.0セキュリティミドルウェアのアップデート Django 6.0では`UpdateCacheMiddleware`にいくつかのセキュリティ修正が含まれています。[2026年のDjangoセキュリティリリース](https://www.djangoproject.com/weblog/2026/jun/03/security-releases/)によると、大文字小文字混在の`Cache-Control: private`を持つレスポンスが誤ってキャッシュされ、`Authorization`ヘッダーを持つリクエストへのレスポンスが共有キャッシュに保存される可能性がありました。 Django 5.1で導入されたLoginRequiredMiddlewareは、サイト全体で認証を強制します: ```python # settings.py MIDDLEWARE = [ # ... other middleware "django.contrib.auth.middleware.LoginRequiredMiddleware", ] # Exempt specific views from django.contrib.auth.decorators import login_not_required @login_not_required def public_view(request): return HttpResponse("Public content") ``` これは保護されたビューに`@login_required`をデコレートする従来のパターンを逆転させます。ほとんどのビューで認証が必要なアプリケーションでは、ボイラープレートを削減し、保護されたエンドポイントの意図しない公開を防ぎます。 ## MiddlewareMixinとレガシーミドルウェアからの移行 `MiddlewareMixin`は、旧スタイルのミドルウェア(`process_request`と`process_response`メソッドを使用)を新しい呼び出し可能パターンに橋渡しします。`django.utils.deprecation.MiddlewareMixin`は非推奨であり、Django 6.2で削除される予定です。代わりに`django.middleware.MiddlewareMixin`を使用してください。 ```python # middleware/legacy_style.py from django.middleware import MiddlewareMixin class LegacyStyleMiddleware(MiddlewareMixin): """Middleware using the legacy hook-based pattern.""" def process_request(self, request): # Called before view, return None to continue request.custom_attribute = "value" return None def process_response(self, request, response): # Called after view, must return response response["X-Custom-Header"] = "middleware-added" return response ``` 新しいミドルウェアでは`MiddlewareMixin`を避けてください。呼び出し可能パターンはより明確な制御フローを提供し、非同期Djangoとの互換性も向上します。 ## Django ミドルウェアの面接でよく聞かれる質問 技術面接では、Djangoのリクエストライフサイクルの理解を評価するためにミドルウェアの質問がよく含まれます。これらの質問は[シニアDjango開発者の面接](/blog/django/django-interview-orm-middleware-drf)で頻繁に登場します。 **Q: ミドルウェアの順序とレスポンスの実行順序の違いは何ですか?** ミドルウェアは`MIDDLEWARE`を通じてリクエストを上から下へ処理しますが、レスポンスは下から上へ処理します。玉ねぎのように考えてください:最も外側のミドルウェアがリクエストを最初に見て、レスポンスを最後に見ます。 **Q: ミドルウェアのショートサーキットはどのように機能しますか?** ミドルウェアが`get_response()`を呼び出す代わりに`HttpResponse`を返すと、リクエストは内側のミドルウェアやビューに到達しません。レスポンスは戻る途中ですべての外側のミドルウェアを通過します。この動作は、すべての`process_response`メソッドがショートサーキットに関係なく実行される旧来の`MIDDLEWARE_CLASSES`設定とは異なります。 **Q: ミドルウェアで`request.user`にアクセスすべきタイミングは?** `AuthenticationMiddleware`が実行された後のみです。認証ミドルウェアがアタッチする前に`request.user`にアクセスすると`AttributeError`が発生します。ユーザー情報が必要なカスタムミドルウェアは、`MIDDLEWARE`リストで`AuthenticationMiddleware`の後に配置してください。 **Q: ミドルウェアを非同期互換にするにはどうすればよいですか?** ミドルウェアクラスに`async_capable = True`を設定し、`await get_response(request)`を使用して`__call__`を非同期メソッドとして実装します。同期と非同期の両方のコンテキストで動作する必要があるミドルウェアには、`@sync_and_async_middleware`を使用し、`iscoroutinefunction(get_response)`をチェックして適切に分岐します。 ## 本番環境と面接に向けたDjangoミドルウェアのマスター Djangoミドルウェアは、リクエストライフサイクル全体にまたがる横断的関心事を制御します。Django 6.0でミドルウェアを扱う際の重要なポイントは以下の通りです: - ミドルウェアはオニオンパターンで実行される:リクエストは上から下へ、レスポンスは下から上へ流れる - ASGIデプロイメントでは`async_capable = True`を設定してスレッドプールのオーバーヘッドを回避する - `process_view`は実行前のビューメタデータ検査に、`process_exception`は一元化されたエラーロギングに使用する - リクエストIDミドルウェアは分散サービス間のログ相関を可能にし、本番環境での一般的な要件である - `LoginRequiredMiddleware`は認証パターンを逆転させる:ビューはデフォルトで保護され、公開ビューは明示的に除外される - 非推奨の`django.utils.deprecation.MiddlewareMixin`はDjango 6.2で`django.middleware.MiddlewareMixin`に移行される - ミドルウェアの順序は、どのコンポーネントが`request.user`、セッションデータ、その他のリクエスト属性を見るかを決定する Django ORMとREST frameworkパターンのより詳細な解説については、[Django ORMクエリ最適化ガイド](/blog/django/django-orm-optimizing-queries)を参照してください。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/django/django-advanced-middleware-logging-2026