2009年1月11日日曜日

xVal

まだバージョンも0.5だし、これからなプロジェクトなんだろうけど、これはちょっと目が離せないかも。

ASP.NET MVCでサーバーサイドのバリデーション(入力検証)を行うとき、DynamicDataで導入されたDataAnnotationsを使うのが、現時点ではベストな選択なんじゃないかと思うんだけど、いかんせんサーバーサイドでの属性ベースのテクノロジなので、クライアントサイドでの検証は別途実装しなきゃなところ。 クライアントサイドでの検証があるだけで、入力をいちいちサーバーにポストしなくても何がエラーなのか、入力と同時に分かるから便利だけど、その為に同じルールを何度もコーディングするのは面倒だよね。面倒だけど仕方なく実装する感じ。

そこを橋渡しするのがこのxVal。

xVal – Home

DataAnnotationsで属性に指定した検証ルールをJson形式に変換して、クライアントに書き出すことで、ルール定義は1箇所で済ませようという、楽したいがタメに生まれたナイスなプロジェクト。 サーバーサイドの属性の抽出用プロバイダとして、System.ComponentModel.DataAnnotationsだけじゃなく、Castle.Components.Validatorも実装。

クライアントサイドの検証にはjQuery.validateの他にも、ASP.NET標準の検証コントロールを使った実装もある(けど、こっちはあんまり興味なし)。xVal.ClientSidePluginsに入ってるこの2つのクライアントサイドの実装は、たぶんこれ以外にも例えば prototype.js版とか作れるってことだよね。AllPossibleRulesを全部書けば...。誰か作ってくれるんじゃないかな~。作ってくれないかな~。

xVal - a validation framework for ASP.NET MVC « Steve Sanderson’s blog ↑ここで、どんな物なのか書かれてる。

サンプルプロジェクトもあるので、実際に動かしてみるのが分かりやすいね。 って、ことで、早速ダウンロード。

BookingsDemoにはxValのアセンブリは含まれてるけど、ソースは含まれてないのでソースはCodePlexから。

xVal - Source Code

なるほど~。すばらしいくらいリフレクション。 DataTypeRule/RangeRule/RegularExpressionRule/RequiredRule/StringLengthRuleの5つのルールに属性を変換するんだね。

サンプルでレンダリングされるJsonは↓。

<script type="text/javascript">
 xVal.AttachValidator("booking",
   {"Fields":[
       {"FieldName":"ClientName","FieldRules":[
           {"RuleName":"StringLength","RuleParameters":{"MaxLength":"15"}},
           {"RuleName":"Required","RuleParameters":{}}]
       },
       {"FieldName":"NumberOfGuests","FieldRules":[
           {"RuleName":"Range","RuleParameters":{"Min":"1","Max":"20","Type":"decimal"}},
           {"RuleName":"DataType","RuleParameters":{"Type":"Integer"}}]
       },
       {"FieldName":"ArrivalDate","FieldRules":[
           {"RuleName":"DataType","RuleParameters":{"Type":"Date"}},
           {"RuleName":"Required","RuleParameters":{}}]
       }]
   })
</script>

見ての通り、入力値の単純な検証は変換するけど、ビジネスルールはサーバーサイドのみで実行。 それが、どこにあるのか探してみると、BookingManager.PlaceBooking。 その中で、属性ベースの検証の実行と、ビジネスルールの検証の両方を実装。

アクションはどうやってるのか見てみると

        [AcceptVerbs(HttpVerbs.Post)]
       public ActionResult CreateBooking(Booking booking)
       {
           try {
               BookingManager.PlaceBooking(booking);              
           }
           catch(RulesException ex) {
               ex.AddModelStateErrors(ModelState, "booking");
           }

           return ModelState.IsValid ? RedirectToAction("Completed")
                                     : (ActionResult) View();
       } 

RuleException が発生(PlaceBooking内でエラーがあったらスロー)したら、ModelStateにエラーを入れる。これで、JavaScriptオフでも入力検証はきちんと実行されるて、エラーフィールドにはinput-validation-error、エラーメッセージも表示(<%= Html.ValidationMessage(モデル名)%>)されるっていうすばらしさ(ModelState.AddModelErrorでエラーを入れれば、Htmlヘルパー経由の場合、ちゃんと反映されるっていうASP.NET MVCの設計がこういうとき威力を発揮するね)。 RCがなかなかリリースされないけど、この辺見てるだけでも楽しいかも~。 ※RCでは動かなかったりするかもしれないけどね。