@Required(msg = @Msg(key = "errors.required"), arg0 = @Arg(key = "LOGIN ID", resource = false)) @Maxbytelength(maxbytelength=20, msg =@Msg(key = "errors.maxbytelength"), arg0 = @Arg(key = "LOGIN ID", resource = false), arg1 = @Arg(key = "20", resource = false)) public String loginId;
SEASAR2 では、上記のように validation を実施していましたが、 このまま Spring Boot へ移植できないものかと思い、試し始めました。
試した結果、なんとなく理解しましたが、今後、気が向いたら、以下のTODOを調べます。
- {0} の記載で、エラーメッセージにフィールド名を埋め込めるようです。(試したが不明)
- 各明細行等の為に、ネストなvalidationがある。(試してない)
- 更に複雑なルールには、独自validation作成が必要。(試してない)
上記以外のポイントは、各srcのcommentに記載しています。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>jp.end0tknr</groupId> <artifactId>MySpring</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MySpring</name> <description>Bukkenkoutei written from SEASAR2 to SpringBoot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion><!--【以下はLOG4J2の為】--> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency><!--【LOMBOK】 --> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency><!--【for JSP】 --> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <!--【VALIDATION】 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency><!--【ログ出力は LOG4J2】--> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <dependency><!--【今回は CustomeStringTrimmerEditor より参照】--> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
src/main/resources/application.properties
# 昔ながらのJSPを使用する為 spring.mvc.view.prefix= /WEB-INF/view/ spring.mvc.view.suffix= .jsp # MessageSource が参照するfileを指定 spring.messages.basename=messages,validation_messages spring.messages.cache-seconds=-1 spring.messages.encoding=UTF-8
src/main/resources/log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <Properties> <Property name="app_name">demo</Property> <Property name="date">%d{yyyy-MM-dd HH:mm:ss.SSS}</Property> <Property name="daily_log">logs/app${app_name}_%d{yyyy-MM-dd}.log</Property> </Properties> <appenders> <Console name="Console" target="SYSTEM_OUT" > <PatternLayout pattern="${date}, [${app_name}], [ %-5level ], %logger{10}, %msg %n" /> </Console> <RollingFile name="File" fileName="logs/app.log" filePattern="${daily_log}.gz"> <PatternLayout pattern="${date}, [${app_name}], [ %-5level ], %logger{10}, %msg %n" /> <Policies> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> </appenders> <loggers> <root level="info"> <appender-ref ref="Console" /> <appender-ref ref="File" /> </root> </loggers> </configuration>
src/main/resources/messages_ja.properties
messages.properties も、空ファイルで必要
login.pleaseinput= 名前を入力してください honorific.title = {0}さん
src/main/resources/validation_messages_ja.properties
validation_messages.properties も、空ファイルで必要
# {0}による フィールド名埋め込み方法は不明でした javax.validation.constraints.NotNull.message= {0} は必須項目です javax.validation.constraints.Size.message= {0}は {min} ~ {max} 文字で入力ください # EL式により、以下のようにも記載できます。 javax.validation.constraints.Size.message_el = \ ${min==max ? min += '文字で入力ください' : \ min==1 ? max += '文字以内で入力ください' : \ min += '~' += max += '文字で入力ください'}
src/main/webapp/WEB-INF/view/login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <form method="post" action="/login"> <hr/> ${msg} <hr/> <div>LOGIN ID 1<input type="text" name="loginId1"></div> <div>LOGIN ID 2<input type="text" name="loginId2"></div> <div>LOGIN ID 3<input type="text" name="loginId3"></div> <button type="submit">SUBMIT</button> </form> </body> </html>
src/main/java/jp.end0tknr/MySpringApplication.java
package jp.end0tknr; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @SpringBootApplication public class MySpringApplication { public static void main(String[] args) { SpringApplication.run(MySpringApplication.class, args); } // validation error 時のメッセージを // validation_messages.properties ファイルから参照する為 @Autowired private MessageSource msgSrc; @Bean public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); localValidatorFactoryBean.setValidationMessageSource(msgSrc); return localValidatorFactoryBean; } }
src/main/java/jp.end0tknr/CustomeStringTrimmerEditor.java
package jp.end0tknr; import java.beans.PropertyEditorSupport; import org.apache.commons.lang3.StringUtils; // <input type="text">からpostされた値を全角空白含め、 // 予め、trimする為のclassです public class CustomeStringTrimmerEditor extends PropertyEditorSupport { private final boolean emptyAsNull; public CustomeStringTrimmerEditor(boolean emptyAsNull) { this.emptyAsNull = emptyAsNull; } @Override public String getAsText() { Object value = this.getValue(); return value != null ? value.toString() : ""; } @Override public void setAsText(String text) { if (text == null) { this.setValue((Object) null); } else { String value = StringUtils.strip(text); if (this.emptyAsNull && "".equals(value)) { this.setValue((Object) null); } else { this.setValue(value); } } } }
src/main/java/jp.end0tknr/LoginAction.java
package jp.end0tknr; import java.util.Locale; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller public class LoginAction { static protected Logger logger = LogManager.getLogger(LoginAction.class); @Autowired MessageSource msgsrc; @RequestMapping(value="/login", method=RequestMethod.GET) public ModelAndView index(ModelAndView modelview) { // messages.properties 内のメッセージへの値の埋め込み String msg_1 = msgsrc.getMessage( "honorific.title", new String[]{"ゲスト"}, Locale.JAPAN); String msg_2 = msgsrc.getMessage( "login.pleaseinput", null, Locale.JAPAN); // jsp にある ${msg} へ埋め込み modelview.addObject("msg",msg_1 +" "+msg_2); modelview.setViewName("login"); return modelview; } @RequestMapping(value="/login", method=RequestMethod.POST) public ModelAndView send( @Validated LoginForm loginForm, BindingResult errors, ModelAndView modelview) { logger.info("START This method."); String msg = ""; // validation errorがある場合、メッセージ等を取得 if(errors.hasErrors()){ logger.error("This method has error."); for (FieldError fieldErr : errors.getFieldErrors()) { msg += "Field:" + fieldErr.getField(); msg += " x Code:" + fieldErr.getCode(); msg += " x Msg:" + fieldErr.getDefaultMessage(); msg += "<hr/>"; } } logger.info("MSG" + msg); modelview.addObject("msg",msg); modelview.setViewName("login"); return modelview; } }
src/main/java/jp.end0tknr/LoginForm.java
package jp.end0tknr; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import lombok.Getter; import lombok.Setter; @Getter @Setter public class LoginForm { @Autowired MessageSource msgsrc; @NotNull(message="{javax.validation.constraints.NotNull.message}") @Size(min=1, max=3, message="{0}は、{min}~{max}文字で入力してください") public String loginId1; @NotNull @Size(min=1, max=3, message="{javax.validation.constraints.Size.message}") public String loginId2; @NotNull @Size(min=1, max=3, message="{javax.validation.constraints.Size.message_el}") public String loginId3; }