質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

29394閲覧

SpringValidatorでチェックした結果メッセージをRESTコントローラで返したい

kojisaiki

総合スコア13

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2016/09/27 08:54

編集2016/09/29 01:19

いつもお世話になっています。

問題点・質問

SpringBootの@RestControllerを使って、WebAPIを提供するシステムを作成しています。

コントローラにおいて、ドメインに次の2つの入力チェックを行おうとしています

  1. 桁数チェック(アノテーション)
  2. 必須チェック(Spring Validator)

エラーが発生した時(@ExceptionHandler(MethodArgumentNotValidException.class)にて)、BindingResult.getDefaultMessageによって、エラーメッセージを取得しようとしています。しかし、

  1. の桁数チェック(アノテーション)ではメッセージプロパティに定義した文章が取得できますが、
  2. の必須チェック(Spring Validator)では取得結果がnullになってしまいます。

Spring Validatorを使ったチェックでも、メッセージプロパティに定義した文章を取得したいです。
MessageSourceからメッセージを取得するタイミングが無いのだと思いますが、どのように実装するのがよいか、お教え頂ければと思います。

コード

  • ドメイン
public class MyFormSpringValidation { @Size(min = 6, max = 20) private String name; ・・・
  • バリデータ
import org.springframework.validation.Validator; @Component public class MyFormSpringValidator implements Validator{ ・・・ @Override public void validate(Object target, Errors errors) { MyFormSpringValidation form = (MyFormSpringValidation) target; String name = form.getName(); if(StringUtils.isEmpty(name)) { errors.rejectValue("name" , "myformspringvalidator.name"); } // その他入力チェック増える予定 ・・・
  • RESTコントローラ
@RestController @RequestMapping("/api") public class MyFormSpringValidationResource { @Inject MyFormSpringValidator myFormSpringValidator; @InitBinder public void initBinder(WebDataBinder binder) { binder.addValidators(myFormSpringValidator); } @RequestMapping(value = "/myformspringvalidation", method = RequestMethod.POST) @Timed public ResponseEntity<MyFormSpringValidation> createForm(@Validated @RequestBody MyFormSpringValidation myform) { ・・・
  • エラー用コントローラ
@ControllerAdvice public class ExceptionTranslator { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public ErrorDTO processValidationError(MethodArgumentNotValidException ex) { BindingResult result = ex.getBindingResult(); List<FieldError> fieldErrors = result.getFieldErrors(); ErrorDTO dto = new ErrorDTO(ErrorConstants.ERR_VALIDATION); for (FieldError fieldError : fieldErrors) { dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage()); } return dto; }
  • エラー用DTO
public class ErrorDTO implements Serializable { private final String message; private final String description; public void add(String objectName, String field, String message) { if (fieldErrors == null) { fieldErrors = new ArrayList<>(); } fieldErrors.add(new FieldErrorDTO(objectName, field, message)); } // 以下、他メンバ、Getter、Setter ・・・
  • メッセージソース
@Configuration public class MessageConfiguration { @Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); messageSource.setBasenames("i18n/messages"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } }
  • メッセージソースをバリデータに設定
@Configuration public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware { @Inject private MessageSource messageSource; @Bean public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setValidationMessageSource(messageSource); return localValidatorFactoryBean; } @Override public Validator getValidator() { return validator(); }
  • メッセージプロパティファイル
myformspringvalidator.name=SpringValidatorでは必須です。 javax.validation.constraints.Size.message=文字数は{min}桁から{max}桁の範囲で入力してください。

試したこと

  • バリデータ内のerrors.rejectValueにおいて、デフォルトメッセージにMessageSourceから取得したメッセージを渡す
import org.springframework.validation.Validator; @Component public class MyFormSpringValidator implements Validator{ ・・・ if(StringUtils.isEmpty(name)) { errors.rejectValue("name" , "myformspringvalidator.name" , messageSource.getMessage("myformspringvalidator.name", null, Locale.getDefault())); }

これはきっと、フレームワークの良しとするところではないかと・・・


ご助力を宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

結果

クライアントに返すErrorDTOを作成する際に、MessageSource.getMessageを実行するようにしました。

  • エラー用コントローラにて
@Inject MessageSource messageSource; public ErrorDTO processValidationError(MethodArgumentNotValidException ex) { BindingResult result = ex.getBindingResult(); List<FieldError> fieldErrors = result.getFieldErrors(); ErrorDTO dto = new ErrorDTO(ErrorConstants.ERR_VALIDATION); for (FieldError fieldError : fieldErrors) { // 変更前 // dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage()); // 変更後 dto.add(fieldError.getObjectName(), fieldError.getField(), messageSource.getMessage(fieldError.getCode(), fieldError.getArguments(), fieldError.getDefaultMessage(), Locale.getDefault())); } return dto; }

理由その1

JSR-303による入力チェック時(javax.validation.constraintsパッケージのアノテーション、Bean Validation)、BindingResult.ErrorへのFieldErrorインスタンス投入は、SpringValidatorAdapterクラスのprocessConstraintViolationsメソッドで行われているようでした。

/** * Process the given JSR-303 ConstraintViolations, adding corresponding errors to * the provided Spring {@link Errors} object. * @param violations the JSR-303 ConstraintViolation results * @param errors the Spring errors object to register to */ protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) { for (ConstraintViolation<Object> violation : violations) { ・・・ bindingResult.addError(new FieldError( errors.getObjectName(), nestedField, rejectedValue, false, errorCodes, errorArgs, violation.getMessage())); ・・・

1つの理由は、この位置と同等のレベルの自作クラスを用意する必要性を感じなかったことです。

理由その2(こっちのほうが実感あり)

そもそも取得しようとしていたFieldError.getDefaultMessageメソッドはあくまでデフォルトメッセージ取得が目的であることに気づきました(名前からしてそうなんですが・・・)。

FieldErrorクラスに用意されたメッセージ関係メソッドが

  • getCode
  • getDefaultMessage

であるところをみると、そもそもこのFieldErrorクラスで取り扱うのはまだ最終的なメッセージではないということです。

私が本来欲しかったのはWebAPIとしてクライアントに返される、システムとして最終的な結果メッセージです。

ということは、アノテーションの場合もSpring Validatorの場合も、FieldErrorの結果を受けて最終的なクライアントへのレスポンスを返すフィルターないしレイヤーが必要ということを感じました。

というわけで

FieldErrorからErrorDTOに詰め替える間に、MessageSource.getMessageを実行することにしました。

投稿2016/10/05 07:22

編集2016/10/05 07:25
kojisaiki

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問