個人的にちょっとハマったのと、Googleで検索しても他にドキュメントがなかったので
備忘録としてメモしておく。
使っているFramework/Libraryのバージョン
- Spring Framework
4.2.2.RELEASE
- Hibernate Validator
5.2.2.Final
書いたコード
以下のようなカスタムバリデーションを記述
```LetterOrDigit.java : java
package jp.ijufumi.validation.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import jp.ijufumi.validation.validator.LetterOrDigitsConstraintValidator;
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = LetterOrDigitsConstraintValidator.class)
public @interface LetterOrDigits {
String message() default "";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
```
``` LetterOrDigitsConstraintValidator.java : java
package jp.ijufumi.validation.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import jp.cotoco.frontend.asp.validation.annotation.LetterOrDigits;
import org.apache.commons.lang3.StringUtils;
/**
* 英数字用バリデーションクラス。
*/
public class LetterOrDigitsConstraintValidator implements ConstraintValidator {
private static final Pattern REGEX_PATTERN = Pattern.compile("^\\w+$");
private String message;
/**
* Initializes the validator in preparation for
* {@link #isValid(Object, ConstraintValidatorContext)} calls.
* The constraint annotation for a given constraint declaration
* is passed.
* <p>
* This method is guaranteed to be called before any use of this instance for
* validation.
*
* @param constraint annotation instance for a given constraint declaration
*/
@Override
public void initialize(LetterOrDigits constraint) {
message = constraint.message();
}
/**
* Implements the validation logic.
* The state of {@code value} must not be altered.
* <p>
* This method can be accessed concurrently, thread-safety must be ensured
* by the implementation.
*
* @param value object to validate
* @param context context in which the constraint is evaluated
* @return {@code false} if {@code value} does not pass the constraint
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return true;
}
if (REGEX_PATTERN.matcher(value).matches()) {
return true;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message);
return false;
}
}
```
起こった事象
以下のようなエラーが発生。
javax.validation.ValidationException: HV000033: At least one custom message must be created if the default error message gets disabled.
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl.getConstraintViolationCreationContexts(ConstraintValidatorContextImpl.java:103) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidationContext.createConstraintViolations(ValidationContext.java:260) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:456) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:127) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:592) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:555) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:490) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:454) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:406) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:204) ~[hibernate-validator-5.2.2.Final.jar:5.2.2.Final]
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:92) ~[spring-context-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.validation.DataBinder.validate(DataBinder.java:869) ~[spring-context-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.validateIfApplicable(ModelAttributeMethodProcessor.java:164) ~[spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:111) ~[spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78) ~[spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162) ~[spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129) ~[spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) [spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) [servlet-api.jar:na]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [servlet-api.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) [catalina.jar:8.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.23]
at org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter.doFilterInternal(OpenEntityManagerInViewFilter.java:178) [spring-orm-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.2.RELEASE.jar:4.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) [catalina.jar:8.0.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:8.0.23]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316) [spring-security-we
原因
### 対策
```ConstraintValidator```の中の```context.disableDefaultConstraintViolation();```を削除するだけ。
``` LetterOrDigitsConstraintValidator.java : java
package jp.ijufumi.validation.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import jp.cotoco.frontend.asp.validation.annotation.LetterOrDigits;
import org.apache.commons.lang3.StringUtils;
/**
* 英数字用バリデーションクラス。
*/
public class LetterOrDigitsConstraintValidator implements ConstraintValidator<LetterOrDigits, String> {
private static final Pattern REGEX_PATTERN = Pattern.compile("^\\w+$");
private String message;
/**
* Initializes the validator in preparation for
* {@link #isValid(Object, ConstraintValidatorContext)} calls.
* The constraint annotation for a given constraint declaration
* is passed.
* <p>
* This method is guaranteed to be called before any use of this instance for
* validation.
*
* @param constraint annotation instance for a given constraint declaration
*/
@Override
public void initialize(LetterOrDigits constraint) {
message = constraint.message();
}
/**
* Implements the validation logic.
* The state of {@code value} must not be altered.
* <p>
* This method can be accessed concurrently, thread-safety must be ensured
* by the implementation.
*
* @param value object to validate
* @param context context in which the constraint is evaluated
* @return {@code false} if {@code value} does not pass the constraint
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return true;
}
if (REGEX_PATTERN.matcher(value).matches()) {
return true;
}
context.buildConstraintViolationWithTemplate(message);
return false;
}
}