SpringでRequestParamの受付

i18nについて書こうと思ったのですがまずこれをやらないと説明していないのを突然出すことに気づいて急遽こちらを作成。前回のサンプルをベースに説明を進めます。

/helloで受け付けるメソッドの実装を見てみます。

@Controller
public class HelloWorld {

	@RequestMapping("/hello")
	public String hello() {
		return "HelloWorld";
	}
}

うーん、シンプル。これは/SampleWebApp/helloをリクエストしたときに実行されるクラス/メソッドですが引数には何もありません。Strutsを知っている方やServletから何かを作ったことがある人ならばHttpServletRequestはどこからとるの?とか思うでしょう。1)自分は思いました。

SpringはRequestMappingで受け付けるメソッドに定義した引数を解釈して柔軟に該当メソッドを呼びます。例えば、先程のhelloメソッドでHttpServletRequestを受け付ける場合は以下のようになります。ついでにParameterを標準出力に出力するコードも入れておきます。

@Controller
public class HelloWorld {

	@RequestMapping("/hello")
	public String hello(HttpServletRequest request) {
		request.getParameterMap().forEach(
				(key,values)->{System.out.printf("%s : %s\n", key, String.join(",", values));});
		return "HelloWorld";
	}
}

先程も書いたとおり、Springはメソッドの引数から柔軟にメソッドを呼び出します。この場合はhelloメソッドの引数からHttpServletRequestオブジェクトを参照できます。引数にHttpServletResponseを加えればHttpServletResponseも参照できます。

試しに/SampleWebApp/hello?foo=bar&try=errorをブラウザから参照してみましょう。2)もちろんcurlとかでもよいです。標準出力には以下のように出力されるかと思います。

foo : bar
try : error

正しくRequestオブジェクトからParameterを参照できてることがわかります。

さて、メソッドの引数に何を指定すれば何が反映されるのか?それはSpringのドキュメント1.4.3 Handler Methodsに書かれています。

@RequestMappingのMethodが柔軟に呼ばれるのもここに書いてますね。

@RequestMapping handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values.

少し下の方の表にまとめられてます。中を見るとServletRequestやServletResponseもありますね。HttpServletRequestが無いようにも見えますがServletRequestの説明に”Choose any specific request type”とあるのでそれっぽいのはいけるでしょう。HttpSessionもServletRequestから取らなくても引数に指定しておけばそのまま使えます。3)一行程度ですけどね。使い方次第、かな?

表の中にはAnnotationもあります。@RequestParamを見るとParameterで指定した値をそのまま取得できそうです。

For access to Servlet request parameters. Parameter values are converted to the declared method argument type. See @RequestParam.

Note that use of @RequestParam is optional, e.g. to set its attributes. See “Any other argument” further below in this table.

@RequestParamについての説明はこちらを参照。Annotationの場合の実装例を説明して終わりにします。4)画面への反映もやりたかったのですがまた今度ですね…。

@Controller
public class HelloWorld {

	@RequestMapping("/hello")
	public String hello(@RequestParam(name="yourName") String name, @RequestParam(name="age") int age) {
		System.out.printf("name: %s\n", name);
		System.out.printf("age : %d\n", age);
		return "HelloWorld";
	}
}

ParameterはyourNameとageの2つ。各々はStringとintで受け付けるようにします。@RequestParamの説明にもあったようにいい感じに変換されます。”/SampleWebApp/hello?yourName=hikaru&age=28″でアクセスすると標準出力には以下のように出力されます。5)hikaruなのはテレビにその人が映ってるから(笑)

name: hikaru
age : 28

特にエラーもなく出力されてますね。先程パラメータの値は適切に変換されると言いましたが”/SampleWebApp/hello?yourName=hikaru&age=0x28″でアクセスするとどうなるかをやってみます。違いはage=28がage=0x28になっているところです。

name: hikaru
age : 40

正しく16進数で解釈しintであるageに渡してますね。どのようなルールで変換しているかは@RequestParamのリンクを辿っていくとその情報が見つかると思います。これは@RequestParamのときにも言えるし(今回は説明していませんが)@ModelAttributeの場合でも同様です。この挙動が良い場合もあれば悪い場合もあるでしょう。入力文字列のValidationとかが気になり始めるかもしれないですね。

さてさて、今回はここらへんで。

References

References
1 自分は思いました。
2 もちろんcurlとかでもよいです。
3 一行程度ですけどね。使い方次第、かな?
4 画面への反映もやりたかったのですがまた今度ですね…。
5 hikaruなのはテレビにその人が映ってるから(笑)