# 画面設計書 24-Chat（WasmOnAspNetCore）

## 概要

本ドキュメントは、WasmOnAspNetCore テストアセットにおけるチャットルームページ（Chat.razor）の画面設計書です。ASP.NET Core SignalR を使用したリアルタイム通信機能のテストを行う Blazor WebAssembly コンポーネントとして実装されています。

### 本画面の処理概要

この画面は、ASP.NET Core と Blazor WebAssembly の統合環境において、SignalR によるリアルタイム双方向通信機能をテストするためのチャットルームページです。画面表示時に自動的に SignalR テストを実行し、サーバーとのメッセージ送受信が正常に機能することを検証します。

**業務上の目的・背景**：本画面は .NET Runtime のテストアセットとして、ASP.NET Core 上でホストされる Blazor WebAssembly アプリケーションにおける SignalR 通信機能を検証するために存在します。WebSocket または Long Polling トランスポートを使用したリアルタイム通信が、WebAssembly 環境で正しく動作することを確認する目的で作成されています。GitHub issue #96546 に関連するデバッグ出力も含まれています。

**画面へのアクセス方法**：アプリケーション起動後、ルートパス `/` にアクセスすることで本画面に遷移します。クエリパラメータとして `transport`（通信方式）と `message`（送信メッセージ）を指定する必要があります。

**主要な操作・処理内容**：
1. 画面表示時に SignalR ハブへの接続を確立
2. クエリパラメータからトランスポート種別とメッセージを取得
3. サーバーへメッセージを送信
4. サーバーからの応答メッセージを受信
5. テスト結果（成功: 0、失敗: -1）を出力

**画面遷移**：本画面は単独で動作するテストページであり、他の画面への遷移機能は実装されていません。テスト完了後、コンソールに結果を出力して終了します。

**権限による表示制御**：本画面は認証・認可による表示制御を実装していません。すべてのユーザーがアクセス可能です。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Mono | 主機能 | Blazor WebAssemblyランタイム上でチャット機能を実行 |
| 14 | System.Net.WebSockets | 主機能 | SignalRによるリアルタイムチャット通信 |
| 59 | WebAssembly Workload | 主機能 | Blazor WebAssemblyアプリケーションのビルドと実行をサポート |
| 13 | System.Net.Http | 補助機能 | SignalRハブへのHTTP接続 |

## 画面種別

テストページ / リアルタイム通信テスト

## URL/ルーティング

- **URL**: `/` （ルートパス）
- **必須クエリパラメータ**:
  - `transport`: 通信方式（"websockets" または "longpolling"）
  - `message`: 送信するメッセージ

## 入出力項目

| 項目名 | 入出力 | データ型 | 必須 | 説明 |
|--------|--------|----------|------|------|
| transport | 入力 | string | はい | クエリパラメータ。通信トランスポート種別 |
| message | 入力 | string | はい | クエリパラメータ。サーバーへ送信するメッセージ |
| result | 出力 | int | - | テスト結果コード（0: 成功、-1: 失敗） |

## 表示項目

| 項目名 | 表示形式 | データソース | 説明 |
|--------|----------|--------------|------|
| ページタイトル | h1 | 固定値 | "Chat Room" |

## イベント仕様

### 1-初回レンダリング後処理（OnAfterRenderAsync）

**トリガー**: コンポーネントの初回レンダリング完了時

**処理フロー**:
1. `OnAfterRenderAsync(bool firstRender)` が呼び出される
2. `firstRender` が true の場合、SignalR テストを開始
3. コンソールに開始ログを出力
4. `SignalRTest.Run()` メソッドを非同期で実行
5. テスト結果をコンソールに出力（`WASM EXIT {result}`）
6. 例外発生時は `WASM EXIT -1` を出力

**データ変更**: なし（コンソール出力のみ）

### 2-SignalR 接続確立（Connect）

**トリガー**: SignalRTest.Run() 実行時

**処理フロー**:
1. クエリパラメータから transport と message を取得
2. ベース URI から SignalR ハブ URL を構築
3. 指定されたトランスポート種別で HubConnection を構築
4. `ReceiveMessage` イベントハンドラを登録
5. `StartAsync()` で接続を確立

### 3-メッセージ送受信（SignalRPassMessages）

**トリガー**: SignalR 接続確立後

**処理フロー**:
1. `SendAsync("SendMessage", message, threadId)` でサーバーへメッセージ送信
2. サーバーの ChatHub で `SendMessage` メソッドが実行
3. サーバーからクライアントへ `ReceiveMessage` イベントが送信
4. クライアントで `ReceiveMessage` ハンドラが呼び出される
5. 接続を破棄し、テスト結果を設定（成功: 0）

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

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

本画面はデータベースとの連携を行いません。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| - | - | - | データベース操作なし |

## メッセージ仕様

| メッセージID | メッセージ種別 | メッセージ内容 | 表示条件 |
|-------------|---------------|---------------|---------|
| MSG-001 | ログ出力 | "ShouldRender = {shouldRender}" | ShouldRender 呼び出し時 |
| MSG-002 | ログ出力 | "SignalRTest is started on CurrentManagedThreadId={threadId}" | テスト開始時 |
| MSG-003 | ログ出力 | "SignalRTest finished with code {result}. WASM EXIT {result}" | テスト正常終了時 |
| MSG-004 | ログ出力 | "SignalRTest failed with exception {ex}. WASM EXIT -1" | テスト例外終了時 |
| MSG-005 | ログ出力 | "Message = [{message}]. ReceiveMessage from server on CurrentManagedThreadId={threadId}" | サーバーからメッセージ受信時 |
| MSG-006 | エラー | "Query string with parameters 'message' and 'transport' is required" | クエリパラメータ不足時 |

## 例外処理

| 例外種別 | 発生条件 | 対処 |
|---------|---------|------|
| Exception | クエリパラメータ不足 | エラーメッセージを表示、終了コード -1 |
| TimeoutException | SignalR テストが2分以内に完了しない | タイムアウトエラー、終了コード -1 |
| Exception | SignalR 接続エラー | 例外内容をログ出力、終了コード -1 |

## 備考

- 本画面は `.NET Runtime` リポジトリのテストアセットであり、ASP.NET Core SignalR と Blazor WebAssembly の統合テストを目的としています。
- `ShouldRender()` のオーバーライドは GitHub issue #96546 のデバッグ用コードです。
- トランスポートは WebSocket と Long Polling をサポートしています。
- サーバーサイドの ChatHub は受信したメッセージに "-pong" を付加して返送します。
- テスト結果は `WASM EXIT {code}` 形式でコンソールに出力され、テストランナーがこの出力を検出します。

---

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

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

### 推奨読解順序

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

SignalR 通信で使用されるメッセージ構造とテスト出力の仕組みを理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TestOutput.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/TestOutput.cs` | テスト出力用のヘルパークラス（1-20行目） |
| 1-2 | SignalRTest.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/SignalRTest.cs` | SignalR テストのメインロジック（1-114行目） |

**読解のコツ**: `TestOutput.WriteLine()` は "TestOutput -> " プレフィックスを付けてコンソール出力します。これはテストランナーが出力を識別するために使用されます。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Program.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/Program.cs` | WebAssemblyHostBuilder の初期化（1-13行目） |
| 2-2 | Chat.razor | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/Pages/Chat.razor` | ページコンポーネントの構造（1-34行目） |

**主要処理フロー**:
1. **Program.cs 8行目**: `WebAssemblyHostBuilder.CreateDefault(args)` でホストを初期化
2. **Program.cs 11行目**: `HttpClient` をスコープドサービスとして登録
3. **Chat.razor 17-32行目**: `OnAfterRenderAsync` で SignalR テストを開始

#### Step 3: SignalR テストロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SignalRTest.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/SignalRTest.cs` | Run メソッド（21-36行目）、Connect メソッド（58-79行目）、メッセージ送受信（95-101行目） |

**主要処理フロー**:
- **21-36行目**: `Run()` メソッドでテスト全体を制御
- **41-56行目**: `GetQueryParameters()` でクエリパラメータを解析
- **58-79行目**: `Connect()` で SignalR ハブへ接続
- **82-92行目**: `StringToTransportType()` でトランスポート種別を変換
- **95-101行目**: `SignalRPassMessages()` でメッセージを送信

#### Step 4: サーバーサイドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ChatHub.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/ChatHub.cs` | SignalR ハブの実装（1-16行目） |

**主要処理フロー**:
- **10-15行目**: `SendMessage()` メソッドでクライアントからのメッセージを受信し、"-pong" を付加して全クライアントへ送信

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

```
WebAssemblyHostBuilder.CreateDefault()
    │
    └─ builder.Build().RunAsync()
           │
           └─ Chat.razor
                  │
                  ├─ ShouldRender() [デバッグ用]
                  │
                  └─ OnAfterRenderAsync(firstRender: true)
                         │
                         └─ SignalRTest.Run()
                                │
                                ├─ GetQueryParameters()
                                │      └─ HttpUtility.ParseQueryString()
                                │
                                ├─ Connect()
                                │      ├─ HubConnectionBuilder
                                │      │      └─ WithUrl(options.Transports)
                                │      │
                                │      ├─ _hubConnection.On("ReceiveMessage")
                                │      │      └─ [メッセージ受信ハンドラ]
                                │      │
                                │      └─ _hubConnection.StartAsync()
                                │
                                └─ SignalRPassMessages()
                                       └─ _hubConnection.SendAsync("SendMessage")
                                              │
                                              ▼
                                       [サーバー: ChatHub.SendMessage()]
                                              │
                                              └─ Clients.All.SendAsync("ReceiveMessage")
                                                     │
                                                     ▼
                                              [クライアント: ReceiveMessage ハンドラ]
                                                     │
                                                     └─ DisposeHubConnection()
                                                            └─ SetResult(0)
```

### データフロー図

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

URL クエリパラメータ
  ├─ transport ────────▶ GetQueryParameters()
  └─ message                    │
                                ▼
                         Connect()
                                │
                                ▼
                         SignalRPassMessages()
                                │
     [クライアント]              │              [サーバー]
           │                    │                   │
           └──── SendAsync ─────┼─────▶ ChatHub.SendMessage()
                                │                   │
                                │                   ▼
           ┌──── ReceiveMessage ┼────── SendAsync("ReceiveMessage")
           │                    │
           ▼                    │
     ReceiveMessage ハンドラ    │
           │                    │
           ▼                    │
     SetResult(0) ─────────────┬───────────▶ コンソール出力
                                              "WASM EXIT 0"
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Chat.razor | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/Pages/Chat.razor` | ソース | チャットページのメインコンポーネント |
| SignalRTest.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/SignalRTest.cs` | ソース | SignalR テストロジック |
| TestOutput.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/TestOutput.cs` | ソース | テスト出力ヘルパー |
| QueryParser.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/Shared/QueryParser.cs` | ソース | クエリパラメータ解析ヘルパー |
| ChatHub.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/ChatHub.cs` | ソース | サーバーサイド SignalR ハブ |
| Program.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/Program.cs` | ソース | クライアントサイドエントリーポイント |
| Program.cs | `src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer/Program.cs` | ソース | サーバーサイドエントリーポイント |
| App.razor | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/App.razor` | ソース | アプリケーションルートコンポーネント |
| MainLayout.razor | `src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient/Layout/MainLayout.razor` | ソース | アプリケーションレイアウト |
