# 付録D 読解ガイド 本書を通じて、HTTP リクエストがサーバに届いてからレスポンスが返るまでの流れを追ってきました。 フレームワークの内部動作を本当に理解するには、最終的にソースコードを読むことが最も確実な方法です。 しかし、大規模なコードベースをいきなり開いても、どこから手をつければよいか分からず挫折しがちです。 本付録では、本書で扱った 3 つのコードベースについて、「最初に読むべきファイル」と「読み進めるための道筋」を紹介します。 ```{tip} ソースコードを読むときは、「全部理解しよう」と焦らないことが大切です。 まずは「リクエストがどう処理されるか」という一本の流れだけを追うことから始めましょう。 ``` --- ## Django のソースをどこから読むか Django のソースコードは GitHub の `django/django` リポジトリにあり、主要なコードは `django/` ディレクトリ以下に配置されています。 全体は巨大ですが、本書の内容に沿って読むなら、入口は明確です。 ### 起点:リクエストの入口 最初に開くべきファイルは `django/core/handlers/wsgi.py` です。 ここに `WSGIHandler` クラスがあり、WSGI サーバから `environ` と `start_response` を受け取って Django の処理に橋渡しする部分になっています。 `__call__` メソッドがリクエストの入口であり、以下の流れが数十行で読み取れます。 1. `WSGIRequest` オブジェクトを生成する 2. `self.get_response(request)` を呼び出す 3. レスポンスを返却する 本書の 2 章・3 章で解説した「WSGI とフレームワークの接続点」がまさにここです。 ```{note} ASGI 側を読みたい場合は、`django/core/handlers/asgi.py` の `ASGIHandler` が対応するクラスです。 `scope`, `receive`, `send` を受け取る構造になっており、WSGI 版と並べて読むと、同じ処理がどのように非同期化されているかが明確に分かります。 ``` ### 次に読む:ミドルウェアチェーン `WSGIHandler.get_response` を追うと、`django/core/handlers/base.py` の `BaseHandler` にたどり着きます。 ここで押さえるべきメソッドは 2 つです。 | メソッド | 役割 | |---|---| | `load_middleware` | `settings.MIDDLEWARE` のリストを逆順にたどり、各ミドルウェアをチェーンさせる | | `_get_response` | URL 解決とビュー呼び出しの核心部分 | 本書の 8 章で解説したミドルウェアの「玉ねぎ構造」が、ここで実装として確認できます。 個別のミドルウェアを読むなら、`django/middleware/security.py`(`SecurityMiddleware`)が最も短く読みやすいファイルです。 `SECURE_SSL_REDIRECT` や `SECURE_HSTS_SECONDS` の処理が数十行で実装されており、14 章で扱ったセキュリティ設定がどう動作するかを具体的に理解できます。 `django/middleware/csrf.py` はより複雑ですが、CSRF トークンの生成・検証の全工程が 1 ファイルに収まっているため、14-5 章の CSRF の理解を深めるのに最適です。 ### URL 解決とビュー呼び出し `django/urls/resolvers.py` には `URLResolver` と `URLPattern` があり、`resolve` メソッドがリクエストパスをビュー関数にマッピングする処理を担っています。 正規表現や `path` コンバータがどう評価されるかをステップ実行で追うと、URL 設計の制約や優先順位が体感できます。 ### リクエスト・レスポンスオブジェクト `django/http/request.py` の `HttpRequest` と `QueryDict`、`django/http/response.py` の `HttpResponse` と `JsonResponse` は、本書の 4 章・5 章に直結するファイルです。 `QueryDict` が `__getitem__` で最後の値だけ返し、`getlist` で全値を返す仕組みは、ソースを読むと「なぜそうなっているのか」が分かります。 ### ORM を読むなら ORM に踏み込む場合は `django/db/models/query.py` の `QuerySet` クラスが起点です。 `filter`, `exclude`, `annotate` などのメソッドが内部的に `Query` オブジェクト(`django/db/models/sql/query.py`)を組み立てていく過程が読めます。 ```{caution} ORM のコードは複雑度が高いため、最初から全部を読もうとすると挫折しやすいです。 まずは `QuerySet.__iter__` を追って「SQL がいつ発行されるか」を確認するところから始めることをおすすめします。 ``` ### 読む順序のまとめ Django を読む際は、以下の順序がおすすめです。 1. `django/core/handlers/wsgi.py` の `WSGIHandler.__call__` でリクエストの入口を押さえる 2. `django/core/handlers/base.py` の `BaseHandler.load_middleware` と `_get_response` でミドルウェアと URL 解決の流れを理解する 3. `django/middleware/security.py` で短いミドルウェアの実装を読む この 3 ステップで、Django がリクエストを受け取ってからビューに到達するまでの全景が見えてきます。 --- ## FastAPI / Starlette のソースをどこから読むか FastAPI は Starlette の上に構築されているため、実行時の HTTP 処理の大部分は Starlette のコードが担っています。 FastAPI 固有のコードは主に以下の部分に集中しています。 - ルーティングの拡張 - 依存性注入 - バリデーション(Pydantic 連携) - OpenAPI スキーマ生成 ```{important} FastAPI のソースを読む際は、「いま読んでいるのは FastAPI の層か Starlette の層か」を常に意識することが重要です。 この 2 層を混同すると、どこで何が起きているか分からなくなりがちです。 ``` ### 起点:ASGI アプリケーションの入口 Starlette 側の入口は `starlette/applications.py` の `Starlette` クラスです。 `__call__` メソッドが `scope`, `receive`, `send` を受け取り、ミドルウェアスタック経由でルーティングに渡す流れが読めます。 FastAPI 側では `fastapi/applications.py` の `FastAPI` クラスが `Starlette` を継承しており、`__init__` で追加のセットアップ(ルーターの差し替え、OpenAPI 設定など)を行っています。ただし `__call__` 自体は Starlette のものがそのまま使われています。 ### ルーティング Starlette のルーティングは `starlette/routing.py` にあり、以下の 3 クラスが中心です。 | クラス | 役割 | |---|---| | `Route` | 1 つのパスとハンドラの対応を表す | | `Router` | 複数の `Route` をまとめ、マッチングを行う | | `Mount` | パスのプレフィックスで別の `Router` をマウントする | `Router.route` メソッド(デコレータ)が `Route` オブジェクトを生成し、`Router.__call__` でリクエストパスとのマッチングを行います。 マッチングのロジックは `Route.matches` メソッドに集約されており、パスパラメータの抽出(`{item_id}` 形式)もここで処理されます。 FastAPI は `fastapi/routing.py` で `APIRoute` と `APIRouter` を定義し、Starlette の `Route` / `Router` を拡張しています。 `APIRoute` の `get_route_handler` メソッドが、リクエストのバリデーション・依存性注入・レスポンスのシリアライズを組み込んだハンドラを生成する箇所です。 FastAPI の「魔法」の多くはここに集約されています。 ### リクエスト・レスポンス `starlette/requests.py` の `Request` クラスは、ASGI の `scope` をラップして以下のようなインターフェースを提供します。 - `request.method` — HTTP メソッド - `request.url` — リクエスト URL - `request.headers` — ヘッダー情報 - `await request.json()` — JSON ボディの取得 付録 B で示した生の `scope` / `receive` 操作がどのように抽象化されているかが直接読めます。 ```{tip} 特に `await request.body()` の実装は読む価値があります。 `receive` のチャンク受信をキャッシュする仕組みが分かり、「ボディは一度しか消費できない」という制約がどのように解決されているかが理解できます。 ``` `starlette/responses.py` には `Response`, `JSONResponse`, `HTMLResponse`, `StreamingResponse` などがあり、いずれも `__call__` メソッドで `send` を呼び出してレスポンスを送信しています。 `StreamingResponse` の実装は、`"more_body": True` を使った段階的送信の実例として読む価値があります。 ### ミドルウェア `starlette/middleware` ディレクトリに各種ミドルウェアがあります。 `starlette/middleware/cors.py`(`CORSMiddleware`)は、本書の 14-8 章で扱った CORS の仕組みが実装レベルで確認できるファイルです。 以下のような処理が読めます。 - プリフライトリクエスト(`OPTIONS`)の処理 - `Access-Control-Allow-Origin` ヘッダーの生成ロジック - ワイルドカード `*` と `allow_credentials` の排他制御 ### 依存性注入 FastAPI の依存性注入は `fastapi/dependencies/utils.py` と `fastapi/dependencies/models.py` に実装されています。 `get_dependant` 関数がエンドポイントの型ヒントを解析し、`Dependant` オブジェクトのツリーを構築する過程が読めます。 ```{admonition} 難易度について class: caution このコードはメタプログラミングが多用されており、難度は高めです。 ただし、`Depends()` がどのように解決されるかを理解するうえで避けて通れない部分でもあります。 最初は「大まかな流れをつかむ」ことを目標に、細部は後回しにしてもよいでしょう。 ``` ### 読む順序のまとめ FastAPI / Starlette を読む際は、以下の順序がおすすめです。 1. `starlette/applications.py` の `Starlette.__call__` で ASGI の入口を押さえる 2. `starlette/routing.py` の `Router.__call__` と `Route.matches` でルーティングの仕組みを理解する 3. `starlette/requests.py` の `Request` クラスで `scope` / `receive` の抽象化を確認する 4. `fastapi/routing.py` の `APIRoute.get_route_handler` で FastAPI 固有の拡張を読む --- ## Werkzeug のソースをどこから読むか Werkzeug は Flask の基盤となっている WSGI ユーティリティライブラリです。 Flask 自体のコードは比較的薄く、HTTP の処理やリクエスト/レスポンスのパース、URL ルーティング、開発サーバなど、重い処理の多くは Werkzeug が担っています。 Flask のソースを読みたい場合も、最終的に Werkzeug のコードにたどり着くことが多いため、Werkzeug を直接読む技術は Flask 理解の土台となります。 ### 起点:リクエストとレスポンス 最初に読むべきファイルは `werkzeug/wrappers/request.py` と `werkzeug/wrappers/response.py` です。 `Request` クラスは WSGI の `environ` 辞書をラップし、以下のような使い慣れたインターフェースを提供します。 - `request.method` — HTTP メソッド - `request.args` — クエリパラメータ - `request.form` — POST データ - `request.headers` — ヘッダー情報 `Request` クラスの中で特に読む価値があるのは `args` プロパティの実装です。 `environ["QUERY_STRING"]` を `MultiDict` にパースする過程が追えます。 ```{note} `MultiDict`(`werkzeug/datastructures/structures.py`)は Django の `QueryDict` に相当するデータ構造です。 同一キーに複数の値が送られてきた場合にどう扱うかという設計思想が読み取れます。 たとえば、フォームで同じ名前のチェックボックスが複数ある場合などがこれにあたります。 ``` `Response` クラスは `__call__(environ, start_response)` メソッドを持っており、それ自体が WSGI アプリケーションとして振る舞えるという設計になっています。 `Content-Type` や `Content-Length` の自動設定、`Set-Cookie` ヘッダーの構築などがこのクラスに集約されています。 ### URL ルーティング `werkzeug/routing/map.py` の `Map` クラスと `werkzeug/routing/rules.py` の `Rule` クラスが URL ルーティングの中心です。 | クラス/メソッド | 役割 | |---|---| | `Map` | 全 URL ルールを管理するコンテナ | | `Rule` | 個別のパスパターンと HTTP メソッドの対応を表す | | `Map.bind` | リクエスト情報にバインドした `MapAdapter` を生成する | | `MapAdapter.match` | パスと HTTP メソッドに基づいてルールをマッチングする | Flask の `@app.route("/users/")` がどう動くかを知りたければ、以下を順番に読むと全体像がつながります。 1. `Rule.__init__` でパスパラメータがどうパースされるか 2. `werkzeug/routing/converters.py` の `IntegerConverter` や `UnicodeConverter` がどう型変換するか ### 開発サーバ `werkzeug/serving.py` は、Flask の `app.run()` で起動する開発サーバの実装です。 Python 標準ライブラリの `http.server` をベースに、マルチスレッド対応やオートリロード機能を追加しています。 ```{warning} 本書の 14-7 章で「開発サーバを本番利用してはいけない」と述べましたが、その理由がソースレベルで確認できます。 以下のような本番には不適切な特性がコードから読み取れます。 - シングルスレッドのリクエスト処理 - デバッグ用のトレースバック表示(エラー内容がブラウザに丸見えになる) - TLS 対応の簡素さ `run_simple` 関数を追うと、リクエストが到着してからレスポンスが返るまでの全工程が 1 ファイルで完結しており、WSGI サーバの最小実装として非常に教育的です。 付録 A で書いた最小 WSGI アプリが、実際のサーバ上でどのように呼ばれるかを具体的に理解できます。 ``` ### デバッガ `werkzeug/debug/__init__.py` の `DebuggedApplication` は、例外発生時にブラウザ上でインタラクティブな Python コンソールを提供する WSGI ミドルウェアです。 これは開発時には便利ですが、本番で有効にすると任意のコード実行が可能になるため、重大な脆弱性となります。 このミドルウェアのコードを読むと、「ミドルウェアで例外をキャッチし、通常とは異なるレスポンスを返す」というパターンの実例として理解でき、同時にセキュリティリスクの具体性も体感できます。 ### 読む順序のまとめ Werkzeug を読む際は、以下の順序がおすすめです。 1. `werkzeug/wrappers/request.py` の `Request` クラスで `environ` の抽象化を理解する 2. `werkzeug/routing/rules.py` の `Rule` と `werkzeug/routing/map.py` の `Map` で URL ルーティングの仕組みを把握する 3. `werkzeug/serving.py` の `run_simple` で開発サーバの全体像を確認する この 3 ステップを経ると、Flask のコード(`flask/app.py` の `Flask` クラス)は、これらの Werkzeug コンポーネントを組み立てる薄いレイヤーとして読めるようになります。 --- ## ソースコードを読むための実践的なテクニック 3 つのコードベースに共通する、ソース読解の実践的なアプローチを最後に紹介します。 **1. リクエストの入口から追う** どのフレームワークも `__call__` メソッドが入口です。そこから `print` や `breakpoint()` を挟みながら実行フローを追うのが最も確実な方法です。 `python -m pdb` や IDE のデバッガで、実際にリクエストを送りながらステップ実行すると、呼び出し順序とデータの変化が手に取るように分かります。 **2. git log と git blame を活用する** あるコードがなぜそう書かれているかは、コミットメッセージや関連する Issue・PR に書かれていることが多いです。 特にセキュリティ修正のコミットは脆弱性の具体的な攻撃手法と対策が記録されており、14 章の内容を深く理解する助けになります。 **3. テストコードを先に読む** テストはそのコードが「何をすべきか」を宣言的に示しています。実装コードを読む前にテストを読むと、期待される振る舞いが把握できます。 | フレームワーク | テストの場所 | |---|---| | Django | `tests/` ディレクトリ | | Starlette | `tests/` ディレクトリ | | Werkzeug | `tests/` ディレクトリ | ```{important} ソースコードを読む力は一朝一夕には身につきません。 しかし、本書で得た HTTP・WSGI/ASGI・ミドルウェア・セキュリティの知識があれば、コードの意図を推測するための土台は十分に整っています。 まずは上記の「最初に読むべきファイル」を 1 つ開くところから始めてみてください。 ```