Visual Studio Express 2012 for Web でいってみる 15.Remote検証を使ってみる
今回は[Remote]検証を使ってみました。
前回まではストアドプロシジャを使ってみたシリーズでしたが、その際やり残していた項目に、「会社名の重複チェック機能」がありました。
データベースのテーブル[運送会社]には会社名(列は運送会社 ややこしい) にはユニーク制約を付けています。現状では、クライアント、サーバーのどちらにも何のチェックも実装されていません。そのため重複した会社名で登録しようとすると
SQLServerからのエラーメッセージをそのまんま表示してます。なんじゃこれ!?ってなりそうです。
[Create]ボタン押下時にサーバー側でのチェックを入れる必要がありますが、[Remote]属性を使ってのリアルタイム検証って機能がMVCにはあるのでこれを試してみます。
参考にさせてもらった情報は
●しばやん雑記 ASP.NET MVC 3 で追加された Compare, Remote 検証属性を使ってみる
http://shiba-yan.hatenablog.jp/entry/20110108/1294476635
●jQuery Validate でキー押下のたびに発生する検証実行を、検証メソッドごとに阻止する
http://devadjust.exblog.jp/13653760/
● MVC 3 and Remote Validation
http://www.deanhume.com/Home/BlogPost/mvc-3-and-remote-validation/51
実装自体は簡単なんで上記の3つを読んでいただければマスターできます。以上。
ですが、せっかくなんで記録のこしておきます。
[Remote]検証機能を使って、既にデータベースに登録されている会社名は使用できなくします。
まず、Modelの変更です。
namespace Mvc4ApplicationD.Models
{
public class 運送会社ViewModel
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[DisplayName("コード")]
public int 運送コード { get; set; }
[Required]
[DisplayName("会社名")]
[StringLength(20, ErrorMessage = "{0} の長さは {1} 文字以内にしてください。")]
[Remote("CheckCarrierName", "運送会社", ErrorMessage = "既に使用されている{0}です。")]
public string 運送会社1 { get; set; }
[DisplayName("電話番号")]
[RegularExpression(@"(0\d{1,4}-|\(0\d{1,4}\) ?)?\d{1,4}-\d{4}"
, ErrorMessage = " 「-」で区切って入力してください。")]
public string 電話番号 { get; set; }
}
}
こんなかんじ。赤文字の部分を追加しました。
引数の1つ目はチェックを行うメソッド名、2つ目はそのメソッドのある[コントローラー名]。3つ目はエラー時に表示するエラーメッセージです。
上記の例だと、会社名の入力時の検証を"運送会社"コントローラの"CheckCarrierName"メソッドで行い、エラー時は"既に使用されている会社名です。と表示することになります。
次はコントローラにチェック機能を実装します。前回までの流れで、dbとのやり取りは、[CarrierRepository.cs]に記述しました。
[運送会社Controller.cs]に以下のメソッドを追加。
//会社名のRemote検証用 public ActionResult CheckCarrierName(string 運送会社1) { return Json(this.repository.IsCarrierNameAvailable(運送会社1), JsonRequestBehavior.AllowGet); }
CarrierRepository.cs]には以下のメソッドを追加。
public bool IsCarrierNameAvailable(string CarrierName) { var nameExist = (from c in this.db.運送会社 where c.運送会社1 == CarrierName select c).Any(); //存在チェック if (nameExist) { return false; } //存在するときはFalse return true; }
前回までの流れもあってこんなサンプルになってしまいました。
ModelのRemote属性で、[運送会社Controller]の[CheckCarrierName]で検証を行う指定がされています。Webページの[会社名]フィールドで会社名を入力しTabキーの押下などでフィールドから抜けるとき、[運送会社Controller]の[CheckCarrierName]が呼び出されます。コントローラはJson型でOKならTrue,ダメならFalseを返しています。上記の例ではわかりにくいですね。もしコントローラーに全部記述したら以下のようになります。尚、テーブル[運送会社]はEntity Data Modelに登録済です。
//会社名のRemote検証用 public ActionResult CheckCarrierName(string 運送会社1) { var nameExist = (from c in this.db.運送会社 where c.運送会社1 == 運送会社1 select c).Any(); //存在チェック if (nameExist) //存在するときはFalse,存在しない(OK)ならTrueを返す。 { return Json(false, JsonRequestBehavior.AllowGet); } return Json(true, JsonRequestBehavior.AllowGet); }
これだけでOK。動かしてみると、
ページが表示されて、会社名に登録済みの[トマト]と入力してtabキーを押下すると、その時点でサーバーでの検証がよびだされ、エラーメッセージが表示されます。
キャレットをトマトの後ろにおきキーボードから[1]を押下します。するとこの時点でサーバーでの検証がよびだされ、エラーメッセージが消去されます。
続けてバックスペースキーを押して[1]を消すと、また検証がよびだされ、エラーメッセージが表示されます。リアルタイムにチェックがかけられるのですが、一旦エラーになるとその後はキー押下の都度サーバーでの検証が呼び出されるようです。
この機能便利なんですが、頻繁に通信が発生するのが心配ですね。対応方法等については冒頭で記載した、『jQuery Validate でキー押下のたびに発生する検証実行を、検証メソッドごとに阻止する』が参考になりそうです。
あと、エラーを返すとき以下のような記述も可能です。
return Json(運送会社1 + " は既に使用されています。", JsonRequestBehavior.AllowGet);
単純にfalseを返すとModelで設定したエラーメッセージが表示されますが、上記の様にエラーメッセージを返すこともできるようです。上記の例だと
トマト はすでに使用されてます。
と表示されます。
『頻繁に発生する通信・検証』は気になるけど、とても簡単便利な機能ですね。
最後に1つ、ささいなことですがちょっとはまった点について。
Modelで属性指定する際
[Remote (メソッド名、コントローラー名)]
このコントローラー名って書いた部分ですが、クラス名じゃないので注意しましょう。
上記の例だと、検証のメソッドを[運送会社Controller]に作成しましたが、
[Remote("CheckCarrierName", "運送会社"]と"Controller"を付けない形式、@Html.ActionLinkのときと同じ風になります。
このミスに気がつかなくて、
Web.configの<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
や、BundleConfig.csの
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
の確認に手間取ってしまいました。このあたりの設定はMVC4の場合初期値でOKで変更は不要ですね。
あと、検証メソッドを置くコントローラーは、ビューとは別に検証用のコントローラを用意してそっちにまとめる手法を取ってるサンプルが多いような気がします。
今回は以上です。