2012年2月7日火曜日

Slim3 Source Code Reading #7

今日はSlim3 Source Code Reading #7でした。

今日の範囲はValidatorの辺りを読みました。
Validatorは入力チェックを行うためのクラスで、以下の型のチェックと、
範囲チェック、長さチェック、必須チェック、正規表現チェックができるようになっています。

  • byte(Validators#byteType())
  • short(Validators#shortType())
  • int(Validators#integerType())
  • long(Validators#longType())
  • float(Validators#floatType())
  • double(Validators#doubleType())
  • Number(Validators#numberType())
  • Date(Validators#dateType())
型チェックはそれぞれ、Byte.valueOf()等を呼び出し、例外がスローされるかどうかで
判定されます。ただし、NumberとDateはそれぞれ、DecimalFormatとSimpleDateFormatクラスでparseした結果、例外がスローされなければOKとなります。

実は、dateType()に関しては気を付けないといけないケースがあります。
以下のテストコードで確認したのですが、日付が不正なケースでもOKと判定される事があります。

    @Test
    public void 日付の確認() {
        input.put("arg1", "2012-02-07");
        input.put("arg2", "2012-02-30");
        input.put("arg3", "2012-02-1a");
        input.put("arg4", "2012-02-0a");
        input.put("arg5", "2012-02-01");
        input.put("arg6", "2012-02-29");
        
        Validators v = new Validators(input);
        v.add("arg1", v.dateType("yyyy-MM-dd"));
        
        assertTrue(v.validate());
        
        // 存在しない日付
        try {
            Validators v2 = new Validators(input);
            v2.add("arg2", v2.dateType("yyyy-MM-dd"));
            
            assertFalse(v2.validate());
        } catch(Exception e) {
            assertTrue(true);
        }
        
        // 日付が不正(2012-02-1aは正しいと判定される)
        try {
            Validators v3 = new Validators(input);
            v3.add("arg3", v3.dateType("yyyy-MM-dd"));
            
            assertTrue(v3.validate());
        } catch(Exception e) {
            assertTrue(true);
        }
        
        // 日付が不正(その2)(2012-02-0aは間違いと判定される)
        try {
            Validators v4 = new Validators(input);
            v4.add("arg4", v4.dateType("yyyy-MM-dd"));
            
            assertFalse(v4.validate());
        } catch(Exception e) {
            assertTrue(true);
        }
        
        // 日付チェック(正しくはこう)
        try {
            Validators v5 = new Validators(input);
            v5.add("arg3", v5.dateType("yyyy-MM-dd"), v5.regexp("^\\d{4}-\\d{2}-\\d{2}$"));
            
            assertFalse(v5.validate());
        } catch(Exception e) {
            assertTrue(true);
        }
        
        Validators v6 = new Validators(input);
        v6.add("arg5", v6.dateType("yyyy-MM-dd"), v6.regexp("^\\d{4}-\\d{2}-\\d{2}$"));
        
        assertTrue(v6.validate());

        Validators v7 = new Validators(input);
        v7.add("arg6", v7.dateType("yyyy-MM-dd"), v7.regexp("^\\d{4}-\\d{2}-\\d{2}$"));
        
        assertTrue(v7.validate());

    }

arg3の"2012-02-1a"という文字列はSimpleDateFormatで変換すると、日付は正しくありませんが、例外がスローされず、日付のインスタンスが返されます。
従って、本来は正しくない日付なのに、正しい日付という判定結果が返ることになります。
したがって、後半のチェックのように、正規表現でのフォーマットチェックも行う必要があります。
※これは両方必要です。正規表現のみだと、arg2のような、存在しない日付を通してしまうからです。

範囲チェックは、Validators#longRangeとValidators#doubleRangeがあります。(小さい範囲の型も同様にこれでチェックすることになると思います)

使い方は、v.longRange(3,10);と言う感じになります。
これだと、3以上、10以下(3と10を含む範囲)がOKとなり、それ以外の値の場合はNGとなります。doubleRangeに関しても使い方は同じです。

必須チェックはValidators#required()を指定する事でチェックがかかります。
nullか""(空文字)の場合、NGとなります。

長さチェックは、Validators#maxlength()、minlength()を利用します。
これは、文字数(バイト数ではない)をカウントして、引数で指定した文字数を超える(maxlength()の場合)か、下回る(minlength()の場合)かすると、NGとなります。
カウントの方法は、String#length()の値を比較しています。

Validationについてはこんな感じで、大した量ではありませんでしたが、ここから、使い方の話題に移りました。

これも今日見つけたのですが、ControllerクラスにasInteger()など、それぞれの型でrequestパラメータを取得する便利なメソッド達があります。
本来の使い方としては、Validatorでチェックをした後、asXXX()を使って、
変数に取り込む使い方が正しい使い方だと思います。
※nullを許容する場合はnullの扱いについても考慮は必要なので、プリミティブ型ではなく、クラスの変数に代入しましょう。
request.getAttribute("param").toString()をわざわざ変換したり、requestScope("param")をわざわざ変換するのは、カッコ悪いという事でした。
(※私のプログラムを見ると、混ざった状態でひどい事が判明したのでリファクタリングします)

asXXX()には、asKey()とasMap()もあります。asKey()はKeyFactory#keyToString()の結果をKeyに変換し、asMap()はHttpServletRequestをRequestMapに変換してくれます。

その後、少しjspのFunctionを見ていたのですが、そこで、blobstoreUrl()を見つけました。
前回の#6の時にBlobstoreのサンプルを見せたのですが、最初のBlobstoreに格納するURLを取得する部分がjsp側でf:blobstoreUrl(リダイレクト先URL)と書くだけで実現
できそうです。

それから、このBlogの記事を+1してくれていた、shin1ogawaさんのGoogle+のコメントがある事に気がついて(今日言われて気がついた)、FileServiceとか、Google Cloud Storageなんかの話題も触れる機会になりました。

それから、Blobstoreの最大ファイルサイズですが、前回32Mと言っていたのですが、
Goの場合(http://goo.gl/1lvU2)だけで、Python/Javaだと2GBまでいけそう(http://goo.gl/mX5Dy)です。

また、私が作ったGAEのアプリケーションで妙なリダイレクト(302のレスポンスを返す)が発生しているケースがあったので、聞いてみたのですが、
どうやら、リクエストのURLの最後の"/"が怪しいようでした。
ログを詳細に分析すると、IndexControllerを実行するURLで、最後に"/"があると、
200を返すのに、"/"がない場合は302を返していました。
IndexControllerでなければ、そういう現象は見受けられませんでした。

したがって、IndexControllerを使う場合は、URLの最後に必ず"/"を入れておかないと不具合の元になりそうな予感がしました。
特に、requestScope的な部分で。

次回は、jspをもう少し見るのと、ModelRef、testerと順番に読んでいく予定です。