LoginSignup
2
1

More than 5 years have passed since last update.

カスタムバリデーションで「javax.validation.ValidationException: HV000033」が発生する原因と対策

Posted at

個人的にちょっとハマったのと、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

原因

context.disableDefaultConstraintViolation();```を実行しているのが原因で、もしこれをやる場合は、```context.buildConstraintViolationWithTemplate(message).addPropertyNode(field).addConstraintViolation();```などを呼び出してあげる必要があった。

### 対策
```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;
    }
}
2
1
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
2
1