GitBucketではWebフレームワークとしてScalatraを使っているのですが、Scalatraにはリクエストパラメータをケースクラスにマッピングしたり、バリデーションしたりするための機能がありません。というわけでPlay2を参考に作ってみました。
まずはこんな感じでケースクラスとマッピングの定義を記述しておきます。Play2と似ていますが、パラメータにフィルタをかけたり、エラーメッセージに項目名を含めたりなど、細かい部分が融通が効くようなインターフェースにしています。
import util.Validations._ case class RegisterForm(name: String, description: String) val form = mapping( "name" -> text(required, maxlength(40)), "description" -> text() )(RegisterForm.apply)
サーブレットはScalatraの提供するScalatraServletではなくフレームワークが提供する基底クラスを継承し、以下のような感じにします。アクションの引数にはリクエストパラメータがマッピングされたケースクラスが渡されます。
class RegisterServlet extends ServletBase { post("/register", form) { form: RegisterForm => ... } }
もしバリデーションエラーの場合、いまのところは例外をスローするようにしています。これはクライアントサイドバリデーションでDBを見なければできないようなチェックも含めサーバサイドでのバリデーションと完全に同じチェックを行うことが可能なためです。
クライアントサイドバリデーションを行うにはビューテンプレート側でform要素にvalidate="true"という属性を追加します。
<form method="POST" action="/register" validation="true"> Name: <input type="name" type="text"> <span class="error" id="error-name"></span> <br/> Description: <input type="description" type="text"> <span class="error" id="error-description"></span> <br/> <input type="submit" value="Register"/> </form>
こうしておくとフレームワークによってsubmit時のイベントハンドラが設定されます。このイベントハンドラでは「formのaction属性の値/validate」というパスにフォームの内容をすべてPOSTします。このパスはフレームワークによって自動的にScalatraに登録されたもので、サーバサイドバリデーションと同じ処理を実行し、バリデーション結果をJSONで返却します。クライアントサイドではこのJSONを受け取ってエラーメッセージを「span#error-フィールド名」にセットします。
というわけで、クライアントサイドバリデーションといっても丸ごとサーバに投げているだけなのでインチキですが、サーバサイドの実装のみでクライアントサイドでも完全に同じチェックができるのは大きいかなと思います。クライアントだけでチェックできる部分とサーバでないとチェックできない部分を分けて考える必要もありませんし、エラー時に入力画面に戻したりといった処理も必要ありません。
ちなみにソースは以下のあたりになります。