Skip to content

MS_ASPNETMVCTerms

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

ASP.NET MVCの用語

概要

MVC

「ASP.NET MVCの用語」は
ASP.NET MVCの利用方法」と比べて、
ASP.NET MVC の基本的なトピックをまとめています。

補足(本ページの前提/最新化): 本ページが扱うのは
.NET Framework 版の ASP.NET MVC 5System.Web.Mvc)である。
ASP.NET Core MVC とは別実装で、
概念は共通だが API 名や既定動作が異なる箇所がある。

主な対応関係(詳細は各節の補足で述べる):

本ページ(ASP.NET MVC 5) ASP.NET Core MVC
ActionResult IActionResult
Html.TextBoxFor タグ ヘルパーasp-for
Ajax.BeginForm 廃止部分描画とJavaScript
RouteConfig.RegisterRoutes app.MapControllerRoute
Bind 属性 [Bind](健在)/DTO 分離が推奨
HandleError 属性 例外ミドルウェア
Global.asax Program.cs

M(Model)に実装される処理

Model には、

  • B 層クラスと、
  • B 層クラスが返す View に渡される Entity, Bean, POCO 的なクラス(ViewModel)

がある。

V(View)に実装される処理

View = 画面表示のための処理。

  • マスタページ的な View と、
  • 全体 View
  • 部分 View がある。

C(Controller)に実装される処理

  • Action Method を実装する。
  • Action Method は、
    1. 「M(Model)」を呼び出して
    2. 「(Bean ≒ M)」を取得して
    3. 「V(View)」呼び出す。

モジュールの作成順

  1. C(Controller)を作成
  2. V(View)を作成
  3. M(Model)を作成
  4. C(Controller)に Action Method を追加し、C→M→V と繋げる。

C(Controller)関連

URL ルーティング

ルート定義

  • ルート定義に従い、URL から Controller や Action Method に処理を振り分ける。
  • ユーザがブラウザに URL を入力すると、
    指定したルーティング規則を使用し、URL が解析され、Controller のパスが特定される。

既定のルート定義

  • 既定のルート定義は、RouteConfig.RegisterRoutes メソッドで定義されている。
  • ココで MapRoute() メソッドを使用し、ルートパラメタ(routeName, routeValues)を登録する。
  • ルートパラメタ(routeName, routeValues)は、URL の作成にも使用される。
  • デフォルトでは以下のようにルート定義されている(自由にカスタマイズ可能)
routes.MapRoute(
    "Default",                     // Route name
    "{controller}/{action}/{id}",  // URL with parameters
    new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);

属性ルーティングによるルート定義

  • 個別のルート定義は、RouteConfig.RegisterRoutes メソッドで定義されている。
  • ココで MapMvcAttributeRoutes() メソッドを使用し、属性ルーティングを有効化する。
routes.MapMvcAttributeRoutes();
  • 以下のようにルート定義可能(自由にカスタマイズ可能)
[Route("{controller}/{action}/{id}", Name="xxxx")]
  • 第一引数には、MapRoute の「URL with parameters」と同じものを指定。

  • 第二引数の Name はオプションで、Html ヘルパー(@Html.RouteLink)で使用できる。

  • また、パラメタについて、以下の様な設定を行うことができる。

    • オプション設定
    • 既定値の設定
    • 制約条件の設定
      • データ型指定
      • 値範囲指定
      • 正規表現指定
      • カスタム制約条件指定
  • その他、モデルレベルに既定の ActionMethod 名を追加可能。

[Route("{action=Top}")]
public class XXXXXController : Controller
{
    public ActionResult Top() { ・・・ }
}

移行メモ(誤記): 原文の [Route("{action=Top")]
閉じ波括弧が欠けているため、[Route("{action=Top}")] に修正した。

ルートプレフィックスの定義

ルートプレフィックスを使用すると、モデルレベルに、
「URL with parameters」のルート部分の定義を追加できる。

補足(ASP.NET Core での書き方): 属性ルーティングは
ASP.NET Core MVC でも同じ考え方で使える
(むしろ API では属性ルーティングが標準)。

[ApiController]
[Route("api/[controller]")]          // ルート プレフィックス
public class ProductsController : ControllerBase
{
    [HttpGet("{id:int}")]            // GET api/products/1
    public IActionResult Get(int id) => Ok();
}

Program.cs 側の規約ルーティングは次の形になる。

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

**{id?}(省略可能)や {id:int}(型制約)**といった
インライン制約が使えるため、
原文が挙げる「制約条件の設定」を属性の中で完結して書ける。

呼び出される Action Method とその引数

この時、ブラウザから

http://server/applicationname/Products/Index/1

という URL でリクエストを送信した場合、

http://(Server FQDN名)/(Controller名)/(Action Method名)/(id 値)

ルート定義に従い、ページハンドラは

  • Controller 名:Products
  • Action Method 名:Index
  • id (Action Method に渡す値):1

と判断し、

Products Controller の Index Action Method を呼び出し、
Action Method の引数として "1" を渡す。
Action Method 名と id 値は省略可能で、Action Method 名を省略すると、
Index Action Method が実行される。

Action Method

リクエストデータの取得方法

  • 引数とのマップの方法

    • 単方向バインディング
      POST や GET のパラメタの名前と一致した引数を定義しておけば、自動的にマップされる。

      • Get ならクエリ文字列のキー名
      • Post ならフォームデータのキー名
      • モデルバインディング
        Model クラスを使うこともできる(Property 名と一致させる)。
    • 双方向バインディング

      • Controller の ActionMethod と View の間に、
        ポストバック的な復元動作がある場合に便利。
      • Model クラスを使う(Post の場合のみ)。
  • その他

    • FormCollection を使う (Post の場合のみ)
      <form> の中身がコレクション型として保持されたもの。

戻り値

Action Method の結果として、Action Result を返す。
詳細はAction Resultを参照。

単方向バインディング

  • クライアントから送信されてきたデータのキー名と、
    Controller の Action Method の引数名とが一致するキー値を探して、バインドする。

  • 非常に単純な仕組みのため、ASP.NET Web Formsに比べて、
    単体テストが遣り易くなっている反面、オーバーポスティング攻撃を受け易く、
    セキュリティ的に脆弱とまでは言えないが、課題があると言える。

  • 仕組みの詳細は、下記を参照。

補足(オーバーポスティング攻撃とは): 原文が繰り返し警告している
この攻撃は、モデル バインディングが「送られてきた項目を素直に埋める」
ことに起因する。

【画面】       名前・メールアドレスの入力欄のみ
【モデル】     class User { Name; Email; IsAdmin; }

攻撃者が POST に IsAdmin=true を追加して送る
  → モデル バインダーが素直に IsAdmin にも値を入れる
  → そのまま保存すると権限昇格

ASP.NET Web Forms では起きにくいのは、
ViewState で「画面に出した項目」が固定されているためである
(原文が「Web Forms に比べて」と書いているのはこの対比)。

対策の優先順位:

対策 評価
① 画面専用の ViewModel(DTO)を使う 最善。危険な項目がそもそも無い
[Bind(Include=...)] で明示 有効だが、項目追加時に漏れやすい
[Bind(Exclude=...)] 危険(除外漏れが起きる)
④ 保存前に手で詰め替える 有効

エンティティをそのまま Action Method の引数にしない
というのが現在の定石である。
ASP.NET Core でも同じ問題は残っている。

ValueProvider

Request を検索して Value を取得するクラス。

  • 既定の ValueProvider
項番 ValueProvider 値取得元
ChildActionValueProvider 子アクション
FormValueProvider フォーム値
RouteDataValueProvider Route データ
QueryStringValueProvider クエリ文字列
HttpFileCollectionValueProvider HTTP ファイルのコレクション
  • その他の ValueProvider
項番 ValueProvider 値取得元
JsonValueProvider Json
CookieValueProvider Cookie
SessionValueProvider Session
ServerVariablesValueProvider ServerVariables
TempDataValueProviderFactory TempData

移行メモ(表記): 原文の「ValuProvider」は
ValueProvider の表記揺れと判断し統一した。

  • ValueProvider の処理順
    上記の既定の ValueProvider は Controller.ValueProvider の順番と同じ
    (同じキー名の場合、ValueProvider の順に先勝になる)

  • ValueProvider の追加
    以下の ValueProviderFactory で順に新規 ValueProvider を追加できる。

    • 実装(Global.Application_Start に実装)
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
ValueProviderFactories.Factories.Add(new SessionValueProviderFactory());
ValueProviderFactories.Factories.Add(new ServerVariablesValueProvider());
ValueProviderFactories.Factories.Add(new TempDataValueProviderFactory());
  • 参考

  • カスタム ValueProvider の自作
    カスタムの ValueProvider を実装するために以下の I/F を使用する。

    • IValueProvider(値プロバイダーに必要なメソッドを定義)
    • IEnumerableValueProvider(列挙に必要なメソッドを定義)
    • IUnvalidatedValueProvider(検証スキップに必要なメソッドを定義)

補足(「先勝ち」が事故のもとになる): 同じ名前のキーが
フォームとクエリ文字列の両方にある場合、
上表の順(Form が先)で決まる。

POST /Edit?id=1   (クエリ文字列に id=1)
Body: id=99       (フォームにも id=99)
  → FormValueProvider が先なので 99 が採用される

どこから取るかを明示するのが安全である。

// ASP.NET MVC 5
public ActionResult Edit([FromUri] int id, MyModel model) { }

// ASP.NET Core(属性が整理されている)
public IActionResult Edit([FromRoute] int id, [FromForm] MyModel model) { }

ASP.NET Core のバインド元属性:

属性 取得元
[FromRoute] ルート パラメータ
[FromQuery] クエリ文字列
[FromForm] フォーム
[FromBody] リクエスト本文(JSON)
[FromHeader] ヘッダー
[FromServices] DI コンテナーASP.NET Core における DI

[ApiController] を付けると、これらが規約で自動推論される
(複合型は [FromBody]、単純型は [FromQuery] 等)。

ModelBinder

  • ModelBinder は、ValueProvider から必要なデータを取得し、
    Model に対して値の設定を行う。

  • DefaultModelBinder は、前述の「既定の ValueProvider」から必要なデータを検索する。

  • カスタム ModelBinder の自作も可能。

ModelBinder 属性

前述の ModelBinder の動作を制御する。

通常、

  • コレクション系は Form
  • プリミティブ型は QueryString

から取得するようになっているが、

以下の属性を Action Method の引数に対して使用すると、この動作を変更できる。

  • FromUri 属性
    QueryString から値を取得するように制御。

  • FromBody 属性
    Form から値を取得するように制御。

移行メモ(FromBody の説明): 原文は
「FromBody 属性:Form から値を取得するように制御」としているが、
正確には [FromBody] は「リクエスト本文(JSON / XML 等)から取得する」
という意味である(Web API 由来の属性)。
フォーム データの取得は既定の動作であり、
ASP.NET Core では [FromForm] が別途用意されている。

Bind 属性

ASP.NET MVCは、ASP.NET Web Formsと比べて、
オーバーポスティング攻撃を受け易いため引数を明記する仕組み。

  • 基礎

    • Bind 属性の引数

    • 設定方法
      Action Methodに以下の様な属性を付与する。

      • [Bind(Include = "PropName1, PropName2...")]
      • [Bind(Exclude = "PropName3, PropName4...")]
      • [Bind(Include = "PropName1, PropName2...", Exclude = "PropName3, PropName4...")]
  • 応用(コレクションを Bind するときの name or key の設定方法)
    http://qiita.com/kazuhisam3/items/94542f6d7ccf3acca41c

    • 配列 or リスト系
      • VariableName[n].PropertyName
      • [n].PropertyName
    • Dictionary 系 :
      • VariableName[n].Key, VariableName[n].Value.PropertyName
      • [n].Key, [n].Value.PropertyName

補足(ASP.NET Core では Include が無くなった): [Bind]
ASP.NET Core にもあるが、引数の形が変わっている

// ASP.NET MVC 5
public ActionResult Create([Bind(Include = "Name,Email")] User user)

// ASP.NET Core(プロパティ名を直接列挙。Exclude は無い)
public IActionResult Create([Bind("Name,Email")] User user)

Exclude が廃止されたのは、
**「除外方式は漏れる」**という前述の理由による。
ホワイトリスト(列挙)のみが残された、と読める。

なお、推奨は依然として ViewModel(DTO)の分離である。

参考

双方向バインディング

  • Controller の ActionMethod と View の間に、ポストバック的な復元動作がある場合に便利。
  • Model クラスを使う(Post の場合のみ)。

@model で定義した Model プロパティと、

Html ヘルパーを使用する。

双方向バインディングの方法

以下のように双方向バインドする。

  • Controller
return View(vm);
  • View スクリプト
@model ViewModel
・・・
Html.TextBoxFor(model => model.Category) 

通常、Model プロパティは、Model でアクセスするが、
ここでの model はラムダ式の仮引数名なので自由。

移行メモ(表記): 原文の @Model ViewModel
**ディレクティブとしては @model(小文字)**が正しい
@Model は「Model プロパティ」を指す)。
原文自身がModel プロパティの節で
@model ViewModelClass と書いているため、
こちらを正として統一した。
また Html.TextBoxFor?(...)? は入力誤りと判断し削除した。

Action Result

Controller の Action Method は、View の選択と指示として、
ActionResult クラスの
オブジェクトを返す必要がある。

Action Method で、

return View();

と、View を指定しない overload で呼び出すと、

/Views/コントローラ名/アクション名.cshtml

を呼び出す。

ActionResult の種類とヘルパー・メソッド

ActionResult クラスには、以下の種類が存在する。

項番 種類 概要・用途 コード例(ヘルパー・メソッド)
ViewResult 指定された全体 View の表示を指示する。
基本的には HTML.BeginForm の場合に使用する。
return View("Result");
("Result" は全体 View 名)
PartialViewResult 指定された部分 View の表示を指示する。
基本的には Ajax.BeginForm の場合に使用する。
return PartialView("Result");
("Result" は部分 View 名)
RedirectResult 指定した URL にリダイレクトする場合に使用する。 return Redirect("http://www.wings.msn.to/");
RedirectToActionResult 指定した Controller, Action にリダイレクトする場合に使用する。 return RedirectToAction("Index");
RedirectToActionResult 指定したルートパラメタ(routeName, routeValues)にリダイレクトする場合に使用する。 return RedirectToRoute("View Product", new { ProductName = <商品名> });
FilePathResult 指定されたパスの内容をファイルとして出力 return File(@"C:\temp\file.zip", "application/zip", "file.zip");
FileContentResult byte 配列の内容をファイルとして出力 return File(bytes, "application/pdf");
FileStreamResult ストリームの内容をファイルとして出力 return new FileStreamResult(fileStream, "application/pdf");
ContentResult プレーン・テキストを出力(CSV 出力等) return Content("こんにちは、世界!", "text/plain");
10 JsonResult 指定されたコンテンツを JSON として出力(Ajax 通信) return Json(JsonConvert.SerializeObject(result), JsonRequestBehavior.AllowGet);
11 JavaScriptResult 指定されたコンテンツを JavaScript スクリプトとして出力 return JavaScript(code);
12 EmptyResult 何もしない

移行メモ(表の展開): 原文は 5 行目の「種類」列を
PukiWiki のセル結合(~)で 4 行目と共有していたが、
GitHub Markdown にセル結合が無いため同じ値を展開した。
また "C:\temp\file.zip" は C# のリテラルとして不正なので
@"C:\temp\file.zip" に修正した。

補足(ASP.NET Core での戻り値): ASP.NET Core では
IActionResult が基本で、ActionResult<T> も使える。

// 型を明示できる(Swagger 生成にも効く)
public ActionResult<Product> Get(int id)
    => _repo.Find(id) is { } p ? p : NotFound();

JSON の返し方が簡潔になっている点も違いである。

// ASP.NET MVC 5:JsonRequestBehavior が必要だった
return Json(obj, JsonRequestBehavior.AllowGet);

// ASP.NET Core:GET でも普通に返せる
return Ok(obj);        // または return obj;([ApiController] 時)

JsonRequestBehavior.AllowGet が不要になったのは、
かつての JSON ハイジャック対策が
現在のブラウザでは不要になったためである。

なお、原文の例が JsonConvert.SerializeObject を渡しているのは
二重シリアライズになるため注意が要る
Json() 自体がシリアライズするので、オブジェクトをそのまま渡す)。

HttpStatusCodeResult

View ではなく、HTTP 状態コードを返す。

項番 種類 概要・用途 コード例(ヘルパー・メソッド)
HttpStatusCodeResult 任意の HTTP 応答コードをセット
HttpUnauthorizedResult HTTP 応答コード「401 Unauthorized」をセット
HttpNotFoundResult HTTP 応答コード「404 NotFound」をセット

ActionResult の自作

可能

属性

フィルタ属性

  • フィルタ属性は、ActionMethod に、以下の様な共通的な処理を追加するために使用する。

    • 認証処理
    • 例外処理
    • ロギング
    • , etc.
  • フィルタ属性は、以下に設定可能である。

項番 適用範囲 設置場所 説明
ActionMethod Filter ActionMethod ActionMethod 単位
Controller Filter Controller Controller 単位
Global Filter FilterConfig Application 単位
  • 以下のフィルタ属性分類があり、実装される処理は項番の順番に呼び出される。
項番 分類 実装する I/F 実装する処理
認証 IAuthenticationFilter 認証に関係する処理
承認 IAuthorizationFilter 承認(認可)に関係する処理
Action IActionFilter ActionMethod の開始処理の前後処理
Result IResultFilter ActionMethod の終了処理の前後処理
例外 IExceptionFilter 例外処理
Override IOverrideFilter 上位フィルタを上書き
  • 標準のフィルタ属性には以下の様なものがある。
項番 分類 属性 概要
承認 Authorize 属性 認証済みアクセス(Cookie 認証、Token 認証)
承認 ChildActionOnly属性 子アクションとしてのみ呼び出し可能に設定
承認 RequireHttps 属性 HTTPS アクセスのみ呼び出し可能に設定
承認 ValidateInput 属性 XSS 対策に使用する。
承認 ValidateAntiForgeryToken 属性 CSRF 対策に使用する。
例外 HandleError 属性 <customErrors mode="On or RemoteOnly"/>
+ FilterConfig に設定した時のカスタムエラーページの定義
Action/例外/Result OutputCache 属性 出力キャッシュルールの定義
Action/Result AsyncTimeout 属性 非同期処理のタイムアウトの定義
Override OverrideAuthentication 属性 グローバル・モデルなど上位で定義されたフィルタを上書き
Override OverrideAuthorization 属性 同上
Override OverrideAction 属性 同上
Override OverrideResult 属性 同上
Override OverrideException 属性 同上

移行メモ(表の展開): 項番 9 の Override 系 5 行は、
原文ではセル結合(~)で項番・分類・概要を共有していたため、
値を展開して掲載した。

補足(ASP.NET Core のフィルターとミドルウェアの使い分け): 概念は
ほぼそのまま引き継がれているが、ミドルウェアという選択肢が増えた
HttpApplication(Global.asax)、HttpModule、HttpHandler)。

【ミドルウェア】  すべてのリクエストが通る。MVC の外側
     → 静的ファイル、認証、CORS、圧縮、例外処理

【フィルター】    MVC のパイプライン内。ModelState 等にアクセスできる
     → 認可、モデル検証、アクション固有のログ

判断の目安: MVC の情報(ModelState、アクション名、
ルート値)が要るならフィルター、要らないならミドルウェア

主な変更点:

ASP.NET MVC 5 ASP.NET Core
IAuthenticationFilter 廃止(認証はミドルウェアへ)
HandleError 属性 UseExceptionHandler ミドルウェア
OutputCache 属性 [ResponseCache] / 出力キャッシュ(.NET 7+)
AsyncTimeout 属性 CancellationToken を受け取る
フィルターの DI [ServiceFilter] / [TypeFilter] で注入できる
// DI を効かせたフィルター
[ServiceFilter(typeof(MyAuditFilter))]
public IActionResult Index() { ... }

セレクタ属性

Controller からの ActionMethod の呼び出しを制御する。

項番 属性 概要
HttpXxxxx 属性 Action Method が受け付ける HTTP Method を指定
AcceptVerbs 属性 Action Method が受け付ける1つ以上の HTTP Method を指定
NonAction 属性 Action Method でないことを明示する。
ActionName 属性 Action Method 名と別名の Action Name を付与する。

セレクタ属性は、自作可能。

非同期 Controller

この技術の登場の背景には C10k problem (C10K 問題)
言うものがある模様。

  • ASP.NET MVC での非同期コントローラーの使用
    https://learn.microsoft.com/ja-jp/previous-versions/aspnet/ee728598(v=vs.98)

    • 非同期 Controller を使用すると、async/awaitを使用し、
      Web サーバのスレッド枯渇を防ぐことができる。

    • 実行に時間のかかる CPU バインド以外の要求に非同期 Controller を使用すると、
      Web サーバの待機スレッドを他に転用可能になるので、Web サーバのスレッド数を節約し、
      スレッド枯渇による「HTTP 503 (サーバーがビジー状態です。)」を防止できる。

    • 図から、リクエスト処理開始時とレスポンス処理終了時のスレッドが変わることが解る。
      後のスレッドは独立したスレッドプールから取得する。複雑なのでオーバーヘッドがある。

内部的には、I/O 完了ポートを使用しているものと思われる。

使い方

  • 旧(AsyncController を継承する。)

    • AsyncController を継承する。
    • 以下の命名規約を守った Action Method を定義する。
      • ActionNameAsync
      • ActionNameCompleted(delegate)
  • Task<ActionResult> を返す。
    MVC 4 以降であれば、以下のように書ける。

    • Action Method 内部で Task を使用する場合、
public Task<ActionResult> Index()
  • Action Method 内部でasync/awaitキーワードを使う場合、
public async Task<ActionResult> Index()

補足(ASP.NET Core では非同期が既定): ASP.NET Core では
AsyncController という区別自体が無く、すべてのコントローラーが
async Task<IActionResult> を返せる

public async Task<IActionResult> Index(CancellationToken ct)
{
    var items = await _repo.ListAsync(ct);
    return View(items);
}

また、ASP.NET config で述べた通り、
同期コンテキストが撤廃されたため、
async/await のデッドロック問題も構造的に解消している。

CancellationToken を引数に取ると、
クライアントが接続を切った時点で処理を打ち切れる
HttpContext.RequestAborted が自動でバインドされる)。

参考

M(Model)関連

ここでは、B 層クラスではなく、

「B 層クラスが返す View に渡される
Entity, Bean, POCO 的なクラス(ViewModel)」

について説明する。

ViewBag, ViewData, TempData

Controller から View にデータを渡すときに使用する入れ物的なモノ。

ViewBag

  • ViewBag は dynamic object。

ViewData

  • ViewData は Dictionary。

TempData

  • TempData は Dictionary。

  • TempData は保持されるため、リダイレクト先に値を渡したいときなどに使う。

  • TempData の詳しい動作は、以下が参考になる。

補足(3 つの関係と、使うべきでない理由): 実体としては、

ViewBag  … ViewData の dynamic ラッパー(同じ物を見ている)
ViewData … ViewDataDictionary(string → object)
TempData … ITempDataDictionary。「1 回読むまで」保持される
生存範囲 型安全
ViewBag / ViewData 同一リクエスト内 × 実行時エラー
TempData 次のリクエストまで(読むと消える) ×
ViewModel(@model 同一リクエスト内 ○ コンパイル時に検出

原則は ViewModel を使うこと。
ViewBag はタイプミスが実行時まで分からないdynamic のため)。

TempData の注意点:

  • 保存先が セッション または Cookie
    → セッションを無効にしていると使えない
  • 読んだ時点で消えるPeek() なら消えない、Keep() で保持)
  • ASP.NET Core では [TempData] 属性でプロパティに付けられる
[TempData] public string Message { get; set; }

リダイレクト後の「登録しました」といったメッセージ表示
(Post/Redirect/Get パターン)が主用途である。

Model プロパティ

View スクリプトから強く型付けされた ViewModel を参照する際に使用する。

使い方

  • 次の旧構文を使用している場合は、
@inherits System.Web.Mvc.WebViewPage<ViewModelClass>
  • 次の構文に置き換える。
@model ViewModelClass
  • @model キーワードで指定した ViewModel に対しては、Model プロパティでアクセスする。

    • Controller から View に Model を渡す。
return View(vm);
  • View スクリプトで Model を使う。
@Model.PropertyName

移行メモ(誤記): 原文の「@Model ViewModel」は、
直前の @model ViewModelClass(ディレクティブ)と
混同した記述と判断し、@Model.PropertyName(プロパティ参照)に修正した。

参考

V(View)関連

View には、

ビューエンジン

Web ページのビューエンジンには以下の 2 つのものがある。

ASPX

従来の ASP.NET と同様、
式やコードブロックを コード・ナゲット(<% ~ %>)で囲む記述形式。
View の拡張子も、従来の ASP.NET と同様、「*.aspx」で表される。

Razor(主流)

ASP.NET MVC 3 で登場したビューエンジン。
式やコードブロックの先頭に「@」を付与する記述形式で、
冗長なコード・ナゲット(<% ~ %>)が不要になる。
View の拡張子は、C# の場合は「.cshtml」、VB の場合は「.vbhtml」で表される。

補足(現在は Razor のみ): ASPX ビューエンジンは
ASP.NET Core MVC には移植されなかった

現在の選択肢は Razor(.cshtml)のみである。
VB の .vbhtml も ASP.NET Core では非対応
(Razor は C# のみ)。

Razor 自体はその後も発展し、

技術 内容
Razor Pages ページ単位(.cshtml + .cshtml.cs)。MVC より軽量
Razor コンポーネント Blazor.razor
Razor クラス ライブラリ ビューを NuGet パッケージで配る

といった形に広がっている。

参考

全体 View

通常の View はコレ。

部分 View

部分 View は、「Partial View」ともいわれ、以下の用途で使われる。

  • 画面の共通化のため
    ASP.NET のユーザーコントロールのように、共通的な画面コンポーネントを部品化しておくもの。
  • Ajax.BeginForm の部分更新の範囲を表すため
    Ajax.BeginForm を使用した非同期処理の場合、
    部分更新の範囲を部分 View で定義する。

配置場所

  • /View/Shared/_XXXXPartial.cshtml
  • /View/Controller名/_XXXXPartial.cshtml (優先)

使用方法(部分 View だけ呼び出す)

  • 通常
@Html.Partial("_XXXXPartial", model)
  • 検索機能無し(仮想パス)
@RenderPage("~/View/Controller名/_XXXXPartial.cshtml", model)
  • HTML 文字列を戻さず、応答ストリームに直接書き出す。
    ViewDataDictionary の独自のコピーを取得するので、親の ViewData には影響を与えない。
@{ Html.RenderPartial("_XXXXPartial", model); }

補足(ASP.NET Core では <partial> / @await: Html.Partial
ASP.NET Core では非推奨(同期実行によるデッドロックの恐れ)で、
次のいずれかを使う。

@* ① タグ ヘルパー(推奨) *@
<partial name="_XXXXPartial" model="Model" />

@* ② 非同期版 *@
@await Html.PartialAsync("_XXXXPartial", Model)
@{ await Html.RenderPartialAsync("_XXXXPartial", Model); }

さらに、ビュー コンポーネント(View Component)が
「子アクション」の後継として用意されている(次節を参照)。

使用方法(Action Method と部分 View の両方を呼び出す)

  • 子 Action Method の定義
    • 子 Action Method の定義は、部分 View が固有データを必要とする場合に追加する。
    • 通常、全体 View を呼び出す Controller に、部分 View の子 Action Method の定義を追加する。
    • Action Method の実行を部分 View の呼び出し時に限定する場合、
      [ChildActionOnly] 属性を追加する。
    • 子 Action Method が ActionResult を返す場合、
      PartialView() ヘルパー・メソッドを使用する。
[ChildActionOnly]  
public ActionResult Current()
{
    ・・・

    return PartialView("_XXXXPartial", partialViewModel);  
}

移行メモ(誤字): 原文の「ActionResult を帰す」は返す
「PartialView() ペルパー・メソッド」はヘルパー
誤変換と判断し修正した。

  • 子 Action Method の呼び出し
    • 通常
@Html.Action("XXXX", model)
  • HTML 文字列を戻さず、応答ストリームに直接書き出す。
    ViewDataDictionary の独自のコピーを取得するので、親の ViewData には影響を与えない。
@{ Html.RenderAction("XXXX", model); }

補足(ASP.NET Core ではビュー コンポーネント): Html.Action /
[ChildActionOnly]ASP.NET Core には無い
代わりに ビュー コンポーネントを使う。

public class CartViewComponent : ViewComponent
{
    private readonly ICartService _svc;      // ← DI が効く
    public CartViewComponent(ICartService svc) => _svc = svc;

    public async Task<IViewComponentResult> InvokeAsync()
        => View(await _svc.GetAsync());
}
<vc:cart />
@* または *@
@await Component.InvokeAsync("Cart")
子アクション(MVC 5) ビュー コンポーネント
実体 コントローラーのアクション 専用クラス
ルーティング URL から直接呼べてしまう[ChildActionOnly] で防ぐ) 呼べない(安全)
DI しにくい コンストラクタ注入
非同期 しにくい async が自然

URL から直接叩けてしまうという子アクションの弱点が
構造的に解消されている点が大きい。

Html ヘルパー

(View ヘルパーという呼称もあるようだが、
ここでは Html ヘルパーという呼称に統一する)

従来の ASP.NET では、ASP.NET Web コントロールを使用して、
ラベルやテキストボックスなどのコントロールを表示していたが、

ASP.NET MVC では、基本的に、以下の様な便利な機能が実装されている
Html ヘルパーを使用する。

主な Html ヘルパー

ASP.NET MVC では、主に以下のような Html ヘルパーが使用できる。

  • 表示のための Html ヘルパー
項番 用途 Html ヘルパー
データの表示 Html.DisplayFor
入力可能なデータの表示 Html.EditorFor
ラベルの表示1 Html.DisplayNameFor
ラベルの表示2 Html.LabelFor
  • HTML タグに対応した Html ヘルパー
項番 HTML タグ Html ヘルパー
フォーム <form> HTML.BeginForm または Ajax.BeginForm
リンク <a> Html.ActionLink
テキストボックス <input type="text"> Html.TextBox または Html.TextBoxFor
テキストエリア <textarea> Html.TextArea または Html.TextAreaFor
パスワード <input type="password"> Html.Password または Html.PasswordFor
チェックボックス <input type="checkbox"> Html.CheckBox または Html.CheckBoxFor
ドロップダウンリスト <select> Html.DropDownList または Html.DropDownListFor
リストボックス <select> Html.ListBox または Html.ListBoxFor
ラジオボタン <input type="radio"> Html.RadioButton または Html.RadioButtonFor
10 隠しフィールド <input type="hidden"> Html.Hidden または Html.HiddenFor
  • URL 生成
項番 用途 Html ヘルパー
「~/...」を仮想パスに変換 Url.Content
Controller、ActionMethod 名などから仮想パスを生成 Url.Action
RouteValueDictionary から仮想パスを生成 Url.RouteUrl

移行メモ(表の見出し): 1 つ目と 3 つ目の表は
原文の見出しが「HTMLタグ」となっていたが、
内容が HTML タグではないため**「用途」**に改めた。

  • なお、ボタンを生成する Html ヘルパーはないため、
    直接 <input type="button"> または <input type="submit"> を記述する。

Model バインディング用の Html ヘルパー

Html.xxxx と Html.xxxxFor の 2 種類の Html ヘルパーがある。

  • Html.xxxx
    Model から View への単方向バインディング
    Html.TextBox("Category") のように、
    プロパティを文字列でマップ指定する場合は、
    "For" がつかない Html ヘルパーを使用する。

  • Html.xxxxFor
    Model ⇔ View の双方向バインディング

    • ...For という名称の Html ヘルパーは、View と Model の間での
      双方向バインディングを行う。
    • Html.TextBoxFor(model => model.Category) のように、
      プロパティをラムダ式でマップ指定する場合は、"For" で終わる Html ヘルパーを使用する。

ただし、HTTP を経由しての双方向バインディングになるので、
処理方式的には、以下のような処理シーケンスになる。

  • POST 時に、Html.TextBoxFor などへの入力値を、自動的に Model に復元する。
  • Controller は、復元された Model から情報を取得して処理を行う。

補足(For 付きを使う理由は型安全性): 原文が挙げる
「双方向バインディング」に加え、コンパイル時に検出できる点が大きい。

@* 文字列指定:タイプミスが実行時まで分からない *@
@Html.TextBox("Categoly")

@* ラムダ式:コンパイル エラーになる *@
@Html.TextBoxFor(m => m.Category)

ASP.NET Core では タグ ヘルパーが推奨される。

<input asp-for="Category" class="form-control" />
<label asp-for="Category"></label>
<span asp-validation-for="Category"></span>

HTML に近い書き方のままバインドできるため、
デザイナーとの分業がしやすい、というのが移行の主な動機である。

Html ヘルパー タグ ヘルパー
見た目 C# のメソッド呼び出し ほぼ HTML
属性の追加 匿名オブジェクト(new { @class = "..." } そのまま属性を書く
IntelliSense あり あり(HTML 補完も効く)

マスターページ

ヘッダーやフッター、サイドメニューなどをアプリケーションで共通的に表示させたい場合、
マスターページを使用してレイアウトを共通化させることができる。
マスターページには、画面ごとに個別実装が必要な箇所を定義する。

ビューエンジン

  • ビューエンジンが ASPX の場合は、従来の ASP.NET と同様 ContentPlaceHolder を使用する。
  • ビューエンジンが Razor の場合は、RenderBody を使用してメインのコンテンツ領域を定義する。

配置場所と使用方法

  • アプリケーション共通
    • 配置場所
      • /View/Shared/_Layout.cshtml
    • 使用方法
      • /View/Shared/_ViewStart.cshtml の Layout プロパティ経由で呼び出される。
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
  • View 単位
    • 配置場所
      • /View/Shared/_XXXXLayout.cshtml
      • /View/Controller名/_XXXXLayout.cshtml (優先)
    • 使用方法

マスターページへのセクションの定義

メインのコンテンツ領域以外に、画面ごとに個別実装が必要な箇所を定義する場合、
RenderSection を使用して「セクション」と呼ばれるサブのコンテンツ領域を定義する。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <!-- メインのコンテンツ領域を描画する場所を定義 -->
    @RenderBody()

    @Scripts.Render("~/bundles/jquery")
    <!-- セクション (サブのコンテンツ領域) を描画する場所を定義 -->
    @RenderSection("scripts", required: false)
</body>
</html>

マスターページの利用

  • 例えば、上記のようにマスターページを定義した場合、
    各コンテンツページでは以下のように実装する。
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";   // 使用するマスターページを指定
}

@* メインのコンテンツ領域 *@
<h2>Index</h2>

@* セクション (サブのコンテンツ領域) *@
@section scripts{
    <script type="text/javascript">
        ...
    </script>
}
  • 上記のように、セクションを実装する場合、
    • C# の場合は「@section <セクション名> { ~ }」、
    • VB の場合は「@Section <セクション名> ~ End Section」

で囲む必要がある。

マスターページのネスト

  • /View/Controller名/YYYY.cshtml の Layout プロパティ経由で呼び出される。
@{
    Layout = "~/Views/Shared/_XXXXLayout.cshtml";
}
@{
    Layout = "~/View/Controller名/_YYYYLayout.cshtml";
}
  • Html ヘルパー・メソッドの第二引数に Layout スクリプト名を指定する。
return View("YYYY", "_XXXXLayout");
return View("YYYY", "_YYYYLayout");

移行メモ(誤記): 原文の return View("YYYY, "_XXXXLayout");
引用符の位置が誤っているため、
return View("YYYY", "_XXXXLayout"); に修正した。

マスターページの選択

  • 以下の section で選択する場合、
    if 文などを使用して、Layout プロパティに設定する *Layout.cshtml を切り替える。
@{
    Layout = "~/Views/Shared/_XXXXLayout.cshtml";
}
  • ActionResult で選択する場合、
    if 文などを使用して、return View() の第二引数に設定する部分 View 名を切り替える。

  • RouteData で選択する場合、以下のように実装する。

if ((HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString() == "Account")
     && (HttpContext.Current.Request.RequestContext.RouteData.Values["Action"].ToString() == "Login"))
{
    Layout = "~/Views/Shared/_Layout2.cshtml";
}
else
{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

BeginForm

BeginForm には以下の 2 つのものがある。

Html.BeginForm の特徴

従来の ASP などで MVC 方式を採用した場合と同じ、画面全体を再描画する仕組み。

@系

@inherits

@modelに置き換えられた。

@model

View スクリプトから強く型付けされた ViewModel を参照する際に使用する。

@section

マスタページを利用する際、定義された section を実装する。

@helper

View スクリプト内にHtml ヘルパーを定義。

補足: @helper は ASP.NET Core の Razor では廃止された。
代替は タグ ヘルパービュー コンポーネント
または @functions 内のローカル関数である。

Razor 系

  • コードナゲット(インライン式)
    式の値の出力
    • 通常のコードナゲット
@...
  • 明示的なコードナゲット
@(...)
  • コードブロック
    • 値の代入
    • メソッド呼び出し
    • オブジェクトの生成
@{...}
  • 制御構文(ネスト可能)
    if, switch, while, for/foreach

    • if
@if(...) {
  ・・・
}
else if (...) {
  ・・・
}
else {
  ・・・
}
  • switch
@switch (i)
{
  case 0:
      ・・・
      break;
  case 1:
      ・・・
      break;
  ・・・
  default:
      ・・・
      break;    
}
  • while
@while (flg)
{
  ・・・
}
  • for
@for (var i = 0; i < 10; i++)
{
  ・・・
}
  • foreach
@foreach (var obj in list)
{
   ・・・
}
  • 静的コンテンツ化
    • 単一行
@:
  • 複数行
<text>・・・</text>
  • コメント
    • サーバーコメント
@* ... *@
  • HTML コメント
<!-- ... -->

補足(Razor の自動エスケープ): 重要な性質として、
@ による出力は自動的に HTML エスケープされる

@Model.Name              @* エスケープされる(安全) *@
@Html.Raw(Model.Html)    @* エスケープされない(XSS に注意) *@

@Html.Raw を使う箇所は、必ず入力元を確認すること。
原文が「Html ヘルパーはサニタイジング処理などを同梱する」と
述べているのは、この自動エスケープを指している。

フォルダ構成

既定のフォルダ構成

ASP.NET MVC のテンプレートは、グルーピングを目的に、
既定で以下のフォルダ構成となっている。
それぞれのフォルダには、以下のようにファイルを配置することが推奨されている。

項番 フォルダ名 配置されるファイル 備考
App_Start 起動時に、初期設定を行うモジュール -
Contents CSS BundleConfig が使用しているため、既定の CSS ファイルは変更しない
Controllers Controller -
Models Model -
Scripts JavaScript BundleConfig が使用しているため、既定の JavaScript ファイルは変更しない
Views View 対応する Controller 名のフォルダ以下に、View のファイルを配置する

Views\(コントローラー名)\Index.cshtml

補足(ASP.NET Core の既定構成): 大きく変わっている。

ASP.NET MVC 5 ASP.NET Core
App_Start\*.cs Program.cs に集約
Content / Scripts wwwroot\(静的ファイルの公開ルート)
Global.asax 無し
Web.config appsettings.json.NET Core config
Controllers / Models / Views 同じ(またはフィーチャー単位に分割)

wwwroot の導入が大きな違いで、
**「ここに置いたものだけが公開される」**という明示的な境界ができた
(ASP.NET では逆に「公開したくないものを塞ぐ」設計だった)。

Area (区分)による分割

Area (区分) とは、ASP.NET MVC アプリケーションを論理的に分割する仕組みのことである。
(ASP.NET MVC プロジェクトをシステム全体とすると、
Area ごとにサブシステム (のようなもの) に分割できる。
App_Start のマップルートが追加されるような感じ、と理解すると分かりやすいかもしれない)

補足(Area は現在も使える): ASP.NET Core でも Area は健在で、
モジュラー モノリス的な分割の手段として使える。

Areas/
  Admin/
    Controllers/  Views/  Models/
  Shop/
    Controllers/  Views/  Models/
[Area("Admin")]
public class UsersController : Controller { }
app.MapControllerRoute(
    name: "areas",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

ただし、規模が大きくなるなら
クラス ライブラリ(Razor クラス ライブラリ)に分ける方が
境界が明確になる、という選択肢もある
VSソリューション プロジェクトの構成検討)。


Tags: 移行, .NET開発, ASP.NET, ASP.NET MVC

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally