結論
アクセスするURLとSpringが受け取るリクエストURLが異なると、FlashAttributeしてもリダイレクト先にパラメータを渡せないぞ!!
事の始まり
例えば、domain/~
のリクエストをdomain/hoge/~
に変更するプロキシが組まれていたとします。
きっとこんな感じ(適当)
location / {
proxy_pass http://tomcat:8080/hoge/;
}
その中で、RedirectAttributesのaddFlashAttributeを実行したところ、リダイレクト先でマッピングされない事態に遭遇しました。
たぶんこんな感じ(さらに適当)
@RequestMapping(value="/hoge/save", method = RequestMethod.POST)
public String save(@ModelAttribute("form") HogeForm form, RedirectAttributes redirectAttributes) {
:
:
redirectAttributes.addFlashAttribute("form", form);
return "redirect:/complete";
}
@RequestMapping(value="/hoge/complete", method = RequestMethod.GET)
public String complete(@ModelAttribute("form") HogeForm form) {
log.info(form); // 空っぽ!!
return "hoge.html";
}
FlashAttributeなんて余裕やろ!と思っていた矢先に起きた問題。
リバースプロキシ方面から調査したのですが、原因が全くわからず。。。
数日後
「RedirectAttributesがダメなら、FlashMapを直接使えばいいじゃない」
なんという暴論!と思いながら実装方法を眺めていると、setTargetRequestPathというメソッドを呼んでいる。。。
・・・ターゲットの・・・・パス?
説明しよう!!
何が起こっていたかというと、リダイレクト先のパス(/complete
)と、リダイレクトされたパス(/hoge/complete
)が不一致であったため、FlashMapの値がバインドされていなかったのだ!!
改修例
こういう改修をすれば、リバースプロキシを設定していてもリダイレクト先へパラメータを渡せるようになる。
protected String redirect(String path, Map<String,Object> attributeMap) {
// 内部パス(@RequestMapping(value)の値)
String innerPath = "/hoge" + path;
// FlashMapに詰め替え
FlashMap flashMap = new FlashMap();
attributeMap.forEach(flashMap::put);
// FlashMapのターゲットを内部パスに設定
flashMap.setTargetRequestPath(innerPath);
// FlashMapのセット
RequestContextUtils.getFlashMapManager(request).saveOutputFlashMap(flashMap, request, response);
// リダイレクト先は外部パス(ブラウザからのアクセス先)
return "redirect:" + path;
}
@RequestMapping(value="/hoge/save", method = RequestMethod.POST)
public String save(@ModelAttribute("form") HogeForm form) {
:
:
return redirect("/complete", Map.of("form", form));
}
rewriteを使っていて、今回のように内部パスと外部パスが単純に置き換えられない場合も、原理は一緒なので頑張って内部パスと外部パスを指定してください。
FlashMapについては、以下の記事でわかりやすく書いてありました。もっと早く見つけたかった。。。
SpringMVCでRedirectAttributesを使用せずにリダイレクト先に値を渡す