情報応用演習Ⅰ(2024)

【T4a】モデルとビューの連携(前編)(6/7)

プロジェクトタイプASP.NET Core Web アプリ(Model-View-Controller)
プロジェクト名T4a
ソリューション名PIT4
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)
注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業については準備作業を参照せよ.

4a-6. エラーメッセージやテクニカルドキュメントの読解

ここまで入力したら実行してみよう. アプリケーションの実行環境として http が選択されていることと, 表示に使用するウェブブラウザが Firefox を選択されていることを確認して実行する.すると_に示すように エラーとなるはずである.

実行結果

これはアクションやビューの表示に際して例外が送出されたことを意味する画面である.ここで重要なアドバイスを 書いておこう.エラーが起きたときは必ずエラーメッセージを読もう

重要なことなのでもう一度書いて強調する.エラーが起きたときは必ずエラーメッセージを読もう. エラーが起きたときに「エラーが起こった」ということしか認識しないのでは解決の糸口をつかむことはできない. エラーメッセージはその原因を示唆するためにシステムやフレームワークが意図的に表示させているものであって, エラーそれ自体は,必ずしも致命的な失敗を意味しているわけではない. むしろ意図しない状況を検出して,そのまま処理を続けてしまうことによって致命的な結果を招かないために エラーを通知して処理を止めているともいえる.

エラーメッセージはこの演習ではほとんどが英文表記となるが,簡単な英文なのできちんと解読してみよう. 読者の中には英語を苦手とするものもいると思われるが,少し厳しい言い方をすれば 「英語が苦手」などと 言っているようではいつまで経っても一人前の技術者にはなれないし,親切な誰かが翻訳してくれることを期待して待っているだけでは 技術的に周回遅れになってしまう .技術英語はそれほど難しくないものなので臆せず読むようにするべきである. では,先ほどのエラー画面の冒頭を引用してみよう.

An unhandled exception occurred while processing the request.
  ArgumentNullException: Value cannot be null. (Parameter 'Model')

  AspNetCoreGeneratedDocument.Views_Home_Index.ExecuteAsync() in Index.cshtml, line 6

これをできるだけ訳してみると以下のようになる.

リクエストを処理している最中に捕捉されない例外が発生した.
  ArgumentNullException: 値は null にできない(パラメータ`Model`)

  AspNetCoreGeneratedDocument.Views_Home_Index.ExecuteAsync() in Index.cshtml, line 6

こういったメッセージの意図を掴むには多少慣れが必要であるが,今回のような 例外メッセージを読むときはその内容発生場所に着目するとよいだろう.エラーの内容は2行目で示唆されているように ArgumentNullException例外である. これは null に対して何らかの操作を行おうとしたときに投じられるものである (参考:プログラミング演習Ⅲ(2023; 金澤クラス)第02回). そしてその「 null であった何か」は,Modelという名前であると報告されている((Parameter 'Model')). また4行目を見てみよう.記述の大半は無視してもよいが重要なのは in Index.cshtml, line 6 の部分である. Index.cshtml というファイルの6行目からエラーが発生している,ということをこのメッセージは示唆している.

実は今回のこのエラーは意図的に起こしているものである. Index.cshtml ,すなわちリスト4a-5-3の 6行目ではモデルクラスが null ではないことをチェックして, null である場合に意図的に例外を投じるようにしている. 前述した通り,@modelディレクティブを指定しているビューでは Modelという変数を通じて,関連付けられているモデルクラスのインスタンスを操作することができるが, コントローラーのアクション(≒メソッド)側の処理でモデルクラスのインスタンスを明示的に指定しない限りModelは null となる

ASP.NET Core MVC のプロジェクトを作成すると,プロジェクト内にHomeというコントローラーがあらかじめ作られている. 今回のこの Index.cshtml というビューに対応するのは,このコントローラーのメソッドなのでそのソースコードを覗いてみよう. Controllers フォルダ内に HomeController.cs というファイルがあるので これを開いてみよう.デフォルトでは_のようになっているはずである.

Homeコントローラの内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using T4a.Models;

namespace T4a.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

強調したIndex()メソッドが問題の「アクション側の処理」である. ASP.NET Core ではhttp://localhost:ポート番号/コントローラー名/アクション名にアクセスすると, まずコントローラー名で指定されるコントローラークラスのアクション名のメソッドが呼び出される.このときにクライアントに 返送されるページ(=HTML)を生成するのにViews/コントローラー名/アクション名.cshtmlが使われるが, これはアクションメソッドの return 文でView()メソッドを呼び出しているためであるView()メソッドは_の18行目,23行目などでそうしているように引数なしで呼び出すこともできるが, この場合は対応するビューにモデルクラスが関連付けられていた場合はModel変数が null となる.

ここでプログラミング技術に関連するドキュメントの読み方についても少し説明しておこう.C#をはじめ ASP.NET Core も Microsoft 社が 提供しているものであり,そのすべての公式情報はマイクロソフトのドキュメントに掲載されている 1. ではView()メソッドの公式のドキュメントを読んでみよう. 前述の「View()メソッド」のリンクをたどってみよう. _のようにView()メソッドにはいくつかのオーバーロード(=引数の組み合わせ違いの同名のメソッド)が存在することが分かる. 引数なしのView()メソッドの作用は先ほど説明した通りであるが,View()メソッドには object型の引数を一つ受け取るオーバーロードが存在している.このオーバーロードに対しては_の ような説明が掲載されているはずである.

Microsoftの公式ドキュメントの例

現状Microsoftのドキュメントは日本語訳もいちおう提供されているが,この日本語訳はほとんどが機械翻訳によるものでありその品質は たいへん低いため英語のまま読むことを強く推奨する.情報技術者としてプログラミング技術にかかわる限り英語ドキュメントを読むことは不可避であるため, 英語のドキュメントはそのまま読めるようにしておこう.慣れないうちは機械翻訳に頼るのもよいが,機械翻訳では必ずしも原文の意味や意図を正確に翻訳してくれるとは限らないので注意が必要である. 機械翻訳による微妙なニュアンスの変質や解釈の誤りが致命的な不具合を生み出してしまう場合もあるため,できるだけ原文をそのまま読むようにしよう.

では前述の object 型の引数を一つ受け取るView()メソッドのオーバーロードの説明を見てみよう.該当部分をそのまま引用する.

View(Object)

Creates a ViewResult object by specifying a model to be rendered by the view.

[Microsoft.AspNetCore.Mvc.NonAction]
public virtual Microsoft.AspNetCore.Mvc.ViewResult View (object? model);
Parameters
model
Object

The model that is rendered by the view.

Returns
ViewResult

The created ViewResult object for the response.

Attributes
NonActionAttribute

このドキュメントではこのメソッドの作用や各々の引数の意味,戻り値といったことを説明している.識別子以外の部分を可能な限り訳すと以下のようになるだろう. 特に強調部分に着目する.

View(Object)

ビューで表示されるmodelを指定して,ViewResultを作成する.

[Microsoft.AspNetCore.Mvc.NonAction]
public virtual Microsoft.AspNetCore.Mvc.ViewResult View (object? model);
引数
model
Object

ビューによって表示されるモデル

戻り値
ViewResult

レスポンスのために作成されたViewResultオブジェクト

Attributes
NonActionAttribute

説明の通り,この引数付きのView()メソッドに何かしらの引数を渡すとそれがビューに関連付けられたモデルとして扱われる_で強調したデフォルトのままのIndexアクションでは,View()メソッドを引数なしで呼び出しているため, ビューで@modelディレクティブが使用されていたとしてもModel変数は null のままとなる.つまり,ビュー側でモデルクラスのインスタンスが 渡されていることを前提とするならば,代わりにこの引数付きのView()メソッドを使用しなければならない,ということである. したがってHomeコントローラのIndexアクションを_に示すように修正する.

Homeコントローラーの追記内容(Indexアクション)
1
2
3
4
5
public IActionResult Index()
{
    return View(new HealthInfo()); // モデルクラスである HealInfo のインスタンスを
                                   // 作ってビューに渡す.
}

ここまで書けたら実行してみよう.図4a-7-2のように入力欄に「0」があらかじめ入力された状態で表示されるはずである.

実行結果

では_に示すように,これらの入力を削除して空欄にしたうえで「送信」ボタンをクリックしてみよう.

すると_のようにエラーメッセージが表示されるはずである. ここですでにクラスの定義から入力内容の制約に関する推定が働いているリスト4a-5-1HealthInfoクラスの定義を見ると,WeightおよびHeightプロパティの型はともに double 型である. ASP.NET Core では, null を代入できない型(=Null非許容な型)のプロパティは基本的に入力必須の項目である とみなされる. 逆に int? 型や string? 型などのように null を代入できる型のプロパティはオプショナル(=任意入力)な項目となる (Null 許容型の概念については本コースの第02回も参照せよ).


  1. 古くからの技術者はよくこのサイトを「MSDNのドキュメント」などと呼ぶことがある ↩︎

Last updated on 2024-04-24
Published on 2024-04-24

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.