LoginSignup
0
0

More than 1 year has passed since last update.

Springbootでメッセージの管理とバリデーション処理を行う

Posted at

以下の内容について記載します

  • メッセージファイルの利用について
  • 単一項目チェック
  • 複数項目チェック

事前準備

GitHub
https://github.com/jirentaicho/springbootsample
※メッセージの表示はコントローラーのアノテーションをRestControllerに変更します。

application.propertiesを
application.ymlとして、ymlファイルで記載を行う。

既存のpropertiesファイル

application.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/misaka
spring.datasource.username=misaka
spring.datasource.password=mikoto

ymlファイルに置き換えた例

application.yml
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/misaka
    username: misaka
    password: mikoto

メッセージを表示する

参考
https://spring-boot-doma2-sample.readthedocs.io/ja/master/messages.html

  • messages.properties
  • ValidationMessages.properties
  • PropertyNames.properties

このうちmessages.propertiesをsrc/main/resourcesに作成します。

messages.properties
anicom.message001=パーフェクトJava
anicom.message002={0}を取得しました。

application.ymlを修正します。
messagesのbasenameに拡張子を省いたファイル名を記載します。

application.yml
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/misaka
    username: misaka
    password: mikoto
  messages:
    basename: messages
    cache-duration: -1
    encoding: UTF-8

設定したメッセージを利用してみます。
適当なコントローラーを作成してメッセージの取得ができるか確認してみます。
{0}のような埋め込みタイプは配列を渡して名称を指定します。
また、メッセージの作成にはMessageSourceを利用します。

InitializeController.java
@RestController
public class InitializeController {

    @Autowired 
    private MessageSource messageSource;

    @RequestMapping("message1")
    public String getMessage1(Model model) {
        String message = messageSource.getMessage("anicom.message001", null, Locale.JAPAN);
        return message;
    }

    @RequestMapping("/message2")
    public String getMessage2(Model model) {
        String message = messageSource.getMessage("anicom.message002", new String[]{"御坂美琴"}, Locale.JAPAN);
        return message;
    }
}

image.png

バリデーション

参考
https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/ja/ArchitectureInDetail/Validation.html

  • 単一フィールド
    • Bean Validation(javax.validation.constraints.NotEmptyを使う)
  • 複数フィールド
    • org.springframework.validation.Validatorインタフェースを実装したValidationクラス

単一フィールド

pomを修正します。spring-boot-starter-validationを追加します。

pom.xml
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
    </dependencies>

コントローラーの引数になるFormクラス(ここではrequestにしています)に対してアノテーションを付与します。
今回はtitleに対してNotEmptyを付けました。

AnimationRequest.java
@Setter
@Getter
@NoArgsConstructor
public class AnimationRequest {

    @NotEmpty
    private String title;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date broadcast_start;

    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date broadcast_end;
}

コントローラーの修正
メソッドの引数に@Validatedアノテーションを付与します。BindingResultにバリデーション結果が入っています。

RegisterController.java
@Controller
public class RegisterController {

    @Autowired
    private AnimationService animationService;

    @PostMapping("/register")
    public String register(@Validated AnimationRequest request, BindingResult bindingResult , Model model) {
        if(bindingResult.hasErrors()) {
            System.out.println(bindingResult);
            System.out.println("error");
            return "redirect:/";
        }
        animationService.insert(request);
        return "redirect:/";
    }

}

メッセージの変更
Validationに関するメッセージは専用のプロパティファイルを作成します。

javax.validation.constraints.NotEmpty.message=タイトルは必須項目です。

apllication.ymlにも反映させます。
basenameにカンマ区切りでファイル名を追加します。

application.yml
spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/misaka
    username: misaka
    password: mikoto
  messages:
    basename: messages,ValidationMessages
    cache-duration: -1
    encoding: UTF-8

エラーを発生させると以下のようなメッセージがコンソールに吐かれます

Field error in object 'animationRequest' on field 'title': rejected value []; codes [NotEmpty.animationRequest.title,NotEmpty.title,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [animationRequest.title,title]; arguments []; default message [title]]; default message [タイトルは必須です。]
error

複数フィールド

少し手順が複雑です。以下のような手順になります。

  1. メッセージを登録する
  2. org.springframework.validation.Validatorを継承したクラスを作成する
  3. コントローラーにInitBinderアノテーションを利用して、WebDataBinderに継承したクラスを登録する

1は必須でないですが、せっかくメッセージをプロパティで管理しているので利用します。

とりあえず、放送開始日に入力があった場合は、放送終了日への入力を必須とするバリデーションを追加します。
image.png

メッセージを登録する

ValidationMessages.propertiesに以下のメッセージを追加します。

ValidationMessages.properties
anicom.error.message001=放送開始日を入力した場合は、放送終了日の入力は必須です。

org.springframework.validation.Validatorを継承したクラスを作成する

実装すべきインターフェースのvalidateをObject型を引数に取ります。
わざわざexecuteメソッドを作る価値はないと思いますが、現状はAnimationValidationクラスなので作りましたが、色々と穴がある状態です(別のformが来たら多分例外発生します)
でも、動きは確認できます。

AnimationValidation.java
package com.volkruss.application.validation;

import java.util.Locale;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.volkruss.application.request.AnimationRequest;

@Component
public class AnimationValidation implements Validator{

    @Autowired
    private MessageSource messageSource;

    @Override
    public boolean supports(Class<?> clazz) {
        // trueにしておく
        return true;
    }

    @Override
    public void validate(Object target, Errors errors) {
        // 動作確認のため汚いですがキャストします。
        execute((AnimationRequest)target,errors);
    }

    /**
     * <P>
     * 相関バリデーションを実行します。
     * <br />
     * 入力値の放送開始日付に入力がある場合は、放送終了日の入力が必須です。
     * </P>
     * 
     * @param request アニメーションリクエスト
     * @param errors エラー
     */
    private void execute(AnimationRequest request,Errors errors) {
        // 放送開始日に入力があって、放送終了日に入力がない場合
        if(Objects.nonNull(request.getBroadcast_start()) && Objects.isNull(request.getBroadcast_end()) ) {
            errors.reject("broadcast_end_date",messageSource.getMessage("anicom.error.message001", null, Locale.JAPAN));
        }
    }

}

コントローラーにInitBinderアノテーションを利用して、WebDataBinderに継承したクラスを登録する

@InitBinderアノテーションを利用して、WebDataBinderに対して先ほど作成したAnimationValidationを登録します。

RegisterController.java
@Controller
public class RegisterController {

    @Autowired
    private AnimationService animationService;

    @Autowired
    private AnimationValidation animationValidation;

    @InitBinder
    public void validatorBinder(WebDataBinder binder) {
        binder.addValidators(animationValidation);
    }

    @PostMapping("/register")
    public String register(@Validated AnimationRequest request, BindingResult bindingResult , Model model) {
        if(bindingResult.hasErrors()) {
            System.out.println(bindingResult);
            System.out.println("error");
            return "redirect:/";
        }
        animationService.insert(request);
        return "redirect:/";
    }

}

これで。放送開始日にだけ入力を入れて登録すると、以下のようなログが出力されます。

org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'animationRequest' on field 'title': rejected value []; codes [NotEmpty.animationRequest.title,NotEmpty.title,NotEmpty.java.lang.String,NotEmpty]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [animationRequest.title,title]; arguments []; default message [title]]; default message [タイトルは必須です。]
Error in object 'animationRequest': codes [broadcast_end_date.animationRequest,broadcast_end_date]; arguments []; default message [放送開始日を入力した場合は、放送終了日の入力は必須です。]
error
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0