連載:ASP.NET MVC入門【バージョン3対応】

第3回 モデル・バインドとアノテーション検証の実装

山田 祥寛(http://www.wings.msn.to/
2011/05/20
Page1 Page2 Page3

検証機能の実装

 入力フォームの実装に当たって、入力値の検証処理は欠かせない。なぜなら、ユーザーとは間違える生き物であるからだ。不正な値によってアプリケーションが予期せぬ動作をしたり、ましてや例外でクラッシュしてしまったりといった状況は、絶対に避けなければならない。また、昨今では、悪意あるユーザーが意図的に不正な値を入力することで、データを盗聴/破壊しようと試みるケースも少なくない。入力値を検証することは、このような攻撃のリスクを抑える、セキュリティ対策の基本でもあるのだ。

 もっとも、このような検証機能を一から実装するのは、なかなか骨の折れる作業だ(実際、ASP.NET MVC 1.0では標準で検証機能を持ち合わせていなかったので、入力フォームの実装にはxValのような外部ライブラリの導入が欠かせなかった)。しかし、ASP.NET MVC 2以降ではアノテーション検証と呼ばれる検証機能が搭載されている。これを利用することで、必須検証や文字列長検証のようなよくある検証処理をごくシンプルなコードで実装できるようになった。

 しかも、デフォルトでクライアントサイド、サーバサイドの双方で検証を実施してくれるのもうれしいポイントだ。ブラウザのスクリプト機能が有効になっている場合にはクライアント側でチェックが行われ、余計なリクエストがサーバに送られることはないし、スクリプト機能が無効になっている場合にも、サーバ側できちんと検証が行われる。つまり、アノテーション検証を利用することで、開発生産性が向上するのみならず、高いパフォーマンスと堅牢さが同時に得られるわけだ。

 本稿後半では、アノテーション検証を利用して、書籍情報の新規作成フォームに検証機能を実装してみよう。実装する検証ルールは、以下のとおりだ。

フィールド 検証内容
Isbn 必須検証、正規表現検証([0-9]{3}-[0-9]{1}-[0-9]{3, 5}-[0-9]{3, 5}-[0-9A-Z]{1})
Title 必須検証、文字列長検証(100文字以内)
Price 範囲検証(100〜10000)
Publish 文字列長検証(30文字以内)
Published 必須検証
表1 実装する検証ルール

 では、具体的な手順を追っていきたい。

■エンティティに検証ルールを追加する

 検証ルールは、前回作成したエンティティのプロパティに属性(アノテーション)として付与するのが基本だ。具体的なコードは、以下のとおり(追加のコードは太字で表している)。

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;

namespace MvcApp.Models
{
  public class Book
  {
    [Key]
    [DisplayName("ISBNコード")]
    [Required(ErrorMessage = "{0}は必須です。")]
    [RegularExpression("[0-9]{3}-[0-9]{1}-[0-9]{3,5}-[0-9]{3,5}-[0-9A-Z]{1}", ErrorMessage = "{0}はISBNの形式で入力してください。")]
    public string Isbn { get; set; }

    [DisplayName("書名")]
    [Required(ErrorMessage="{0}は必須です。")]
    [StringLength(100, ErrorMessage="{0}は{1}文字以内で入力してください。")]
    public string Title { get; set; }

    [DisplayName("価格")]
    [Range(100, 10000, ErrorMessage="{0}は{1}〜{2}の間で入力してください。")]
    public int? Price { get; set; }

    [DisplayName("出版社")]
    [StringLength(30, ErrorMessage="{0}は{1}文字以内で入力してください。")]
    public string Publish { get; set; }

    [DisplayName("刊行日")]
    [Required(ErrorMessage = "{0}は必須です。")]
    public DateTime Published { get; set; }

    public virtual ICollection<Review> Reviews { get; set; }
  }
}
Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel

Public Class Book
  <Key()>
  <DisplayName("ISBNコード")>
  <Required(ErrorMessage:="{0}は必須です。")>
  <RegularExpression("[0-9]{3}-[0-9]{1}-[0-9]{3,5}-[0-9]{3,5}-[0-9A-Z]{1}", ErrorMessage:="{0}はISBNの形式で入力してください。")>
  Public Property Isbn() As String

  <DisplayName("書名")>
  <Required(ErrorMessage:="{0}は必須です。")>
  <StringLength(100, ErrorMessage:="{0}は{1}文字以内で入力してください。")>
  Public Property Title() As String

  <DisplayName("価格")>
  <Range(100, 10000, ErrorMessage:="{0}は{1}〜{2}の間で入力してください。")>
  Public Property Price() As Integer?

  <DisplayName("出版社")>
  <StringLength(30, ErrorMessage:="{0}は{1}文字以内で入力してください。")>
  Public Property Publish() As String

  <DisplayName("刊行日")>
  <Required(ErrorMessage:="{0}は必須です。")>
  Public Property Published() As DateTime

  Public Overridable Property Reviews() As ICollection(Of Review)
End Class
リスト4 検証ルールを追加したBookエンティティ(上:Book.cs、下:Book.vb)

 ASP.NET MVC 3で利用できる主な検証属性を以下にまとめておく。

属性 概要
Required() 必須検証
StringLength(max) 文字列長検証(文字列長がmax文字以内か)
Range(min, max) 値範囲検証(値がmin〜maxの範囲に収まっているか)
Compare(other)* 比較検証(値がほかのプロパティotherと等しいか)
Remote(action, controller)* リモート検証(指定されたアクションで正否を判定)
RegularExpression(pattern) 正規表現検証(文字列が正規表現パターンpatternにマッチするか)
CustomValidation(type, method) カスタム検証(クラスtypeのmethodメソッドで検証)
表2 ASP.NET MVC 3で利用できる主な検証属性(*はバージョン3で追加されたもの)

 これら検証属性のうち、サンプルで紹介できなかったCompare、Remote、CustomValidationについては、次回あらためて解説の予定だ。

 なお、それぞれの検証属性で指定している名前付き引数ErrorMessageは、エラー時に表示するメッセージを表すものだ。エラー・メッセージには、{0}、{1}……のようにプレイスホルダを埋め込める点に注目してほしい。{0}はプロパティの表示名を、{1}、{2}……は検証に必要なパラメータ(文字列長検証であれば、最大の文字列長)を表す。

 プロパティの表示名は、あらかじめDisplayName属性で宣言しておく必要がある。ここで指定された表示名は、Html.LabelForなどのビュー・ヘルパーからも参照されるものであるので、検証の有無にかかわらず、画面に表示するすべてのプロパティで指定しておくのが望ましいだろう(指定がない場合は、元のプロパティ名が利用される)。

■アクション・メソッドで検証の成否をチェックする

 スキャフォールディング機能で作成したアプリケーションをそのまま利用するならば、これだけで最低限の検証機能は動作してしまう。よって、ここからはすでに生成済みの検証コードを読み解いていくことにしよう。今後、自分で一からコードを記述する場合の理解に役立ててほしい。

[HttpPost]
public ActionResult Create(Book book)
{
  if (ModelState.IsValid)
  {
    …データベース処理…
  }
  return View(book);
}
<HttpPost()>
Function Create(book As Book) As ActionResult
  If ModelState.IsValid Then
    …データベース処理…
  End If
  Return View(book)
End Function
リスト5 検証の成否を確認するためのコード(上:BooksController.cs、下:BooksController.vb)

 もっとも、コントローラ側の記述はごくシンプルなもので、注目すべきは太字の部分だけだ。ModelStateオブジェクト(厳密にはModelStateプロパティが返すModelStateDictionaryオブジェクト)は、モデル側で発生したエラー情報を管理するためのオブジェクトである。検証結果は、このIsValidプロパティでもって確認が可能だ。

 リスト5の例では、IsValidプロパティがtrue(すべての検証が成功)である場合にのみデータベースへの登録処理を行い、false(何らかのエラーが発生)の場合はViewメソッドで入力フォーム(Create.cshtml/Create.vbhtml)を再描画しているわけだ。検証結果に応じて処理を分岐するこの書き方は、データベース登録の定石でもあるので、きちんと理解しておいてほしい。


 INDEX
  ASP.NET MVC入門【バージョン3対応】
  第3回 モデル・バインドとアノテーション検証の実装
    1.データ登録の基本/Createアクション・メソッド/ビュー・スクリプト
  2.検証機能の実装/エンティティに検証ルールを追加
    3.ビュー・スクリプトでエラー・メッセージを表示
 
インデックス・ページヘ  「ASP.NET MVC入門【バージョン3対応】」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間