# 画面設計書 42-リダイレクト

## 概要

本ドキュメントは、Symfony FrameworkBundleが提供する汎用リダイレクトコントローラー（RedirectController）の画面設計書である。ルーティング設定で指定されたルート名またはURLへHTTPリダイレクトを実行する汎用的な仕組みを提供する。

### 本画面の処理概要

**業務上の目的・背景**：Webアプリケーション開発において、URLの変更やリソースの移動に伴うリダイレクト処理は頻繁に発生する。RedirectControllerは、専用のコントローラークラスを作成せずにルーティング設定だけでリダイレクト処理を実現するため、URL構造の変更やドメイン移行、旧URLの互換性維持、HTTPSへの強制リダイレクトなどの要件に対応できる。

**画面へのアクセス方法**：ルーティング設定（YAML/XML/PHP/アトリビュート）でRedirectControllerを指定し、`route`パラメータ（ルート名指定）または`path`パラメータ（URL指定）を設定する。ユーザーは設定されたURLパスにHTTPリクエストを送信することでリダイレクトが実行される。

**主要な操作・処理内容**：
1. `redirectAction()`: 指定されたルート名からURLを生成し、そのURLへリダイレクトする
2. `urlRedirectAction()`: 指定されたパスまたはURLへ直接リダイレクトする
3. `__invoke()`: ルーティングパラメータから`route`または`path`を判定し、適切なアクションに委譲する
4. 永続的リダイレクト（301/308）と一時的リダイレクト（302/307）の切り替えが可能
5. HTTPメソッドの保持オプション（keepRequestMethod）に対応

**画面遷移**：本コントローラーはリダイレクト専用であり、画面を表示しない。入力元URLから出力先URL（routeまたはpath指定）へリダイレクトする。

**権限による表示制御**：RedirectController自体には権限制御機能は組み込まれていない。権限制御はSymfonyのセキュリティコンポーネントでルーティングレベルに設定する。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | FrameworkBundle | 主機能 | RedirectControllerによるルートまたはURLへのリダイレクト処理 |
| 3 | Routing | 主機能 | Routing（UrlGenerator）による指定ルート名からのURL生成とリダイレクト |
| 1 | HttpFoundation | 補助機能 | RedirectResponseの生成とHTTPステータスコードの管理 |

## 画面種別

リダイレクト（画面表示なし）

## URL/ルーティング

ルーティング設定により任意のURLを割り当て可能。以下はルーティング設定例：

```yaml
# ルート名によるリダイレクト
legacy_homepage:
    path: /old-home
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
    defaults:
        route: app_homepage
        permanent: true

# URLパスによるリダイレクト
external_redirect:
    path: /go-to-docs
    controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController
    defaults:
        path: https://symfony.com/doc
        permanent: false
```

## 入出力項目

### redirectAction（ルート名指定リダイレクト）

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|------|------|-------------|------|
| route | string | はい | - | リダイレクト先のルート名 |
| permanent | bool | いいえ | false | 永続的リダイレクトかどうか（true: 301/308、false: 302/307） |
| ignoreAttributes | bool\|array | いいえ | false | 無視するルーティング属性（falseで全属性転送、配列で指定属性を除外） |
| keepRequestMethod | bool | いいえ | false | HTTPリクエストメソッドを保持するか（true: 307/308、false: 301/302） |
| keepQueryParams | bool | いいえ | false | クエリパラメータを保持するか |

### urlRedirectAction（URL指定リダイレクト）

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|------|------|-------------|------|
| path | string | はい | - | リダイレクト先の絶対パスまたはURL |
| permanent | bool | いいえ | false | 永続的リダイレクトかどうか |
| scheme | string\|null | いいえ | null | URLスキーム（nullで現在のスキームを維持） |
| httpPort | int\|null | いいえ | null | HTTPポート番号 |
| httpsPort | int\|null | いいえ | null | HTTPSポート番号 |
| keepRequestMethod | bool | いいえ | false | HTTPリクエストメソッドを保持するか |

## 表示項目

リダイレクト処理のため、画面表示は発生しない。ブラウザはHTTPリダイレクトレスポンスを受信し、Location ヘッダーで指定されたURLに自動遷移する。

## イベント仕様

### 1-ルート名指定リダイレクト（redirectAction）

HTTPリクエスト受信時に以下の処理が実行される：

1. ルート名が空文字の場合、HttpException（永続: 410 Gone、一時: 404 Not Found）をスローする
2. `ignoreAttributes`がfalseまたは配列の場合、リクエストのルーティング属性を取得する
3. `keepQueryParams`がtrueの場合、クエリパラメータをルーティング属性にマージする
4. 内部パラメータ（route, permanent, ignoreAttributes, keepRequestMethod, keepQueryParams）を属性から除外する
5. `ignoreAttributes`が配列の場合、指定された属性を除外する
6. UrlGeneratorでルート名と属性から絶対URLを生成する
7. RedirectResponseを生成して返却する

### 2-URL指定リダイレクト（urlRedirectAction）

HTTPリクエスト受信時に以下の処理が実行される：

1. パスが空文字の場合、HttpException（永続: 410 Gone、一時: 404 Not Found）をスローする
2. ステータスコードを決定する（permanent + keepRequestMethodの組み合わせ）
3. スキームが未指定の場合、現在のリクエストスキームを使用する
4. パスが`//`で始まる場合、スキームを付加する
5. パスが完全なURL（スキーム付き）の場合、そのままリダイレクトする
6. 相対パスの場合、現在のホスト名、ポート、ベースURL、クエリストリングを組み合わせて完全なURLを構築する
7. RedirectResponseを生成して返却する

### 3-自動振り分け（__invoke）

`__invoke()`メソッドが呼ばれた場合：

1. ルーティングパラメータから`route`キーと`path`キーの存在を確認する
2. 両方存在する場合、RuntimeExceptionをスローする
3. `route`キーが存在する場合、`redirectAction()`に委譲する
4. `path`キーが存在する場合、`urlRedirectAction()`に委譲する
5. どちらも存在しない場合、RuntimeExceptionをスローする

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| リダイレクト | なし | なし | データベースへのアクセスは発生しない |

## メッセージ仕様

| メッセージ種別 | 条件 | メッセージ内容 |
|-------------|------|-------------|
| HTTP 301 | permanent=true, keepRequestMethod=false | Moved Permanently（ブラウザ標準メッセージ） |
| HTTP 302 | permanent=false, keepRequestMethod=false | Found（ブラウザ標準メッセージ） |
| HTTP 307 | permanent=false, keepRequestMethod=true | Temporary Redirect（ブラウザ標準メッセージ） |
| HTTP 308 | permanent=true, keepRequestMethod=true | Permanent Redirect（ブラウザ標準メッセージ） |

## 例外処理

| 例外クラス | 発生条件 | HTTPステータス | 処理内容 |
|-----------|---------|-------------|---------|
| HttpException | route が空文字（permanent=true） | 410 Gone | リソースが永久に削除されたことを示す |
| HttpException | route が空文字（permanent=false） | 404 Not Found | リソースが見つからないことを示す |
| HttpException | path が空文字（permanent=true） | 410 Gone | リソースが永久に削除されたことを示す |
| HttpException | path が空文字（permanent=false） | 404 Not Found | リソースが見つからないことを示す |
| \RuntimeException | routeとpathの両方が指定されている | - | 設定の曖昧さを示すエラー |
| \RuntimeException | routeもpathも指定されていない | - | 必須パラメータ不足エラー |

## 備考

- RedirectControllerは`final`クラスとして宣言されており、継承による拡張は不可である
- `__invoke()`メソッドが実装されているため、ルーティング設定でアクション名を省略できる
- HTTPステータスコードの決定マトリクス：
  - permanent=false + keepRequestMethod=false → 302
  - permanent=true + keepRequestMethod=false → 301
  - permanent=false + keepRequestMethod=true → 307
  - permanent=true + keepRequestMethod=true → 308
- `keepQueryParams`はredirectAction()でのみサポートされ、urlRedirectAction()ではリクエストのクエリストリングが自動的にパスに付加される
- コンストラクタでUrlGeneratorInterface、httpPort、httpsPortをオプショナルに受け取る

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

RedirectControllerが操作するHTTPリクエスト・レスポンスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RedirectResponse.php | `src/Symfony/Component/HttpFoundation/RedirectResponse.php` | リダイレクトレスポンスの構造、Locationヘッダーの設定方法を理解する |
| 1-2 | Request.php | `src/Symfony/Component/HttpFoundation/Request.php` | リクエストオブジェクトのattributes、query、serverプロパティの役割を理解する |

**読解のコツ**: RedirectResponseはResponseを継承しており、コンストラクタでLocationヘッダーを自動設定する。

#### Step 2: エントリーポイントを理解する

RedirectControllerの処理フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RedirectController.php | `src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php` | コントローラー全体の処理フローを理解する |

**主要処理フロー**:
1. **L30-35**: コンストラクタ - UrlGeneratorInterface、httpPort、httpsPortの注入
2. **L53**: `redirectAction()`のシグネチャ - route, permanent, ignoreAttributes, keepRequestMethod, keepQueryParams
3. **L55-57**: 空ルート名のチェックとHttpExceptionスロー
4. **L59-77**: ルーティング属性の処理（ignoreAttributes, keepQueryParams対応）
5. **L79-83**: ステータスコードの決定（permanent + keepRequestMethodの組み合わせ）
6. **L85**: UrlGeneratorでURL生成しRedirectResponseを返却
7. **L106**: `urlRedirectAction()`のシグネチャ
8. **L108-110**: 空パスのチェックとHttpExceptionスロー
9. **L118-122**: スキームプロトコル相対URL（`//`始まり）の処理
10. **L125-127**: 完全URL判定とリダイレクト
11. **L129-135**: クエリストリングの付加処理
12. **L137-162**: ポート番号の決定とURL構築
13. **L164-166**: 最終URLの構築とRedirectResponse返却
14. **L169-186**: `__invoke()`メソッド - route/pathの判定と適切なアクションへの委譲

#### Step 3: URL生成処理を理解する

ルート名からURLを生成する仕組みを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | UrlGeneratorInterface.php | `src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php` | URL生成インターフェースの仕様、ABSOLUTE_URL定数の意味を理解する |

**主要処理フロー**:
- `generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL)`: ルート名と属性から絶対URLを生成する

### プログラム呼び出し階層図

```
RedirectController::__invoke()
    |
    +-- [routeキーが存在] RedirectController::redirectAction()
    |       |
    |       +-- Request::attributes->get('_route_params')
    |       +-- HeaderUtils::parseQuery() [keepQueryParams時]
    |       +-- UrlGeneratorInterface::generate($route, $attributes, ABSOLUTE_URL)
    |       +-- new RedirectResponse($url, $statusCode)
    |
    +-- [pathキーが存在] RedirectController::urlRedirectAction()
            |
            +-- Request::getScheme()
            +-- parse_url($path, PHP_URL_SCHEME)
            +-- Request::getHost() / getPort() / getBaseUrl()
            +-- Request::server->get('QUERY_STRING')
            +-- new RedirectResponse($url, $statusCode)
```

### データフロー図

```
[入力]                          [処理]                              [出力]

HTTPリクエスト -------> ルーティング解決 -------> RedirectController
                        (route/path,              |
                         permanent,               |
                         keepRequestMethod,       v
                         ...)             [route指定] UrlGenerator::generate()
                                          [path指定] URL構築ロジック
                                                  |
                                                  v
                                           RedirectResponse
                                           (Location: リダイレクト先URL)
                                           (Status: 301/302/307/308)
                                                  |
                                                  v
                                           HTTPリダイレクトレスポンス -------> クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RedirectController.php | `src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php` | ソース | 汎用リダイレクトコントローラー本体（全187行） |
| RedirectResponse.php | `src/Symfony/Component/HttpFoundation/RedirectResponse.php` | ソース | HTTPリダイレクトレスポンスクラス |
| Request.php | `src/Symfony/Component/HttpFoundation/Request.php` | ソース | HTTPリクエストクラス |
| HeaderUtils.php | `src/Symfony/Component/HttpFoundation/HeaderUtils.php` | ソース | HTTPヘッダーユーティリティ（クエリパース） |
| UrlGeneratorInterface.php | `src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php` | ソース | URL生成インターフェース |
