はじめに
表題の件についてまとめます。APIの仕様をちゃんと確認しよう、というお話です。
SpringBootのValidationで「{0}」がそのまま出力される
バリデーションエラー時にプレースホルダを利用してエラーメッセージを出力しようとしました。
「{0}」を使えば、フィールド名が出力されることを期待して、jakarta.validation.constraints.NotNull.message=「{0}」は必須入力です
のようにValidationMessages.properiesへ記述したところ、表示された文字は「{0}」は必須入力です
となりました。
これでハマること2時間。。。
原因
エラーメッセージを出力しようとしていたコードは次のとおりです。
【略】
@PostMapping
public ResponseEntity<?> postSomething(@Validated @RequestBody SomethingForm somethingForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
StringBuilder errorMessage = new StringBuilder();
for (ObjectError error : bindingResult.getAllErrors()) {
errorMessage.append(error.getDefaultMessage()).append("; ");
}
return ResponseEntity.badRequest().body(errorMessage.toString());
}
stocks.add(stocksAddForm);
return ResponseEntity.ok("Stock info added successfully!");
}
ValidationMessages.properiesの記述の仕方や@Configを追加したりと色々試してもうまくいかず、途方に暮れていたところ、ついにたどり着いた部分がこれです。
error.getDefaultMessage()
そもそもエラーメッセージの出力がどのようにして行われているのか、はたと気づいて調べた結果、通常、BindingResult.getAllErrors()
で取得したエラーメッセージは、プレースホルダーが解決されていない状態で返されることが分かりました。
うまくいかないのも当たり前です。。。
解決
コードを次のように修正しました。
【略】
+ private final MessageSource messageSource;
+
+ public StocksController(MessageSource messageSource) {
+ this.messageSource = messageSource;
+ }
@PostMapping
- public ResponseEntity<?> postStocks(@Validated @RequestBody StocksAddForm stocksAddForm, BindingResult bindingResult) {
+ public ResponseEntity<?> postStocks(@Validated @RequestBody StocksAddForm stocksAddForm, BindingResult bindingResult, Locale locale) {
if (bindingResult.hasErrors()) {
+ StringBuilder errorMessage = new StringBuilder();
for (ObjectError error : bindingResult.getAllErrors()) {
- errorMessage.append(error.getDefaultMessage()).append("; ");
+ String message = messageSource.getMessage(error, locale);
+ errorMessage.append(message).append("; ");
}
return ResponseEntity.badRequest().body(errorMessage.toString());
}
stocks.add(stocksAddForm);
return ResponseEntity.ok("Stock info added successfully!");
}
MessageSourceのgetMessage()
メソッドを使うことで、プレースホルダが正しく解釈されて出力されました。
結論
適当に引っ張ってきたコードをそのまま使わず、その意味をきちんと丁寧に理解しなければならないと痛感しました。。。
よく分からないところに原因は潜んでいるのかも。