JBoss.orgCommunity Documentation

Chapter 48. Validation

48.1. Bean Validation 1.1
48.2. Bean Validation 1.0
48.3. Validation Service Providers

RESTEasy provides the support for validation mandated by the JAX-RS: Java API for RESTful Web Services 2.0 , given the presence of an implementation of the Bean Validation specification 1.1 such as Hibernate Validator 5.x.

Validation provides a declarative way of imposing constraints on fields and properties of beans, bean classes, and the parameters and return values of bean methods. For example, in

@Path("all")
@TestClassConstraint(5)
public class TestResource
{
   @Size(min=2, max=4)
   @PathParam("s")
   String s;

   private String t;

   @Size(min=3)  
   public String getT()
   {
      return t;
   }

   @PathParam("t") 
   public void setT(String t)
   {
      this.t = t;
   }

   @POST
   @Path("{s}/{t}/{u}")
   @Pattern(regexp="[a-c]+")
   public String post(@PathParam("u") String u)
   {
      return u;
   }
}

the field s is constrained by the Bean Validation built-in annotation @Size to have between 2 and 4 characters, the property t is constrained to have at least 3 characters, and the TestResource object is constrained by the application defined annotation @TestClassConstraint to have the combined lengths of s and t less than 5:

@Constraint(validatedBy = TestClassValidator.class)
@Target({TYPE})
@Retention(RUNTIME)
public @interface TestClassConstraint
{
   String message() default "Concatenation of s and t must have length > {value}";
   Class<?>[] groups() default {};
   Class<? extends Payload>[] payload() default {};
   int value();
}

public class TestClassValidator implements ConstraintValidator<TestClassConstraint, TestResource>
{
   int length;

   public void initialize(TestClassConstraint constraintAnnotation)
   {
      length = constraintAnnotation.value();
   }

   public boolean isValid(TestResource value, ConstraintValidatorContext context)
   {
      boolean b = value.retrieveS().length() + value.getT().length() < length;
   }
}

See the links above for more about how to create validation annotations.

Also, the method parameter u is constrained to have no more than 5 characters, and the return value of method post is constrained by the built-in annotation @Pattern to match the regular expression "[a-c]+".

The sequence of validation constraint testing is as follows:

  1. Create the resource and validate field, property, and class constraints.
  2. Validate the resource method parameters.
  3. If no violations have been detected, call the resource method and validate the return value

If any constraint violations are detected, an instance of org.jboss.resteasy.api.validation.RESTEasyViolationException will be returned:

public class RESTEasyViolationException extends ValidationException
{  
   ...
      
   public Exception getException()
   {
      return exception;
   }

   public List<RESTEasyConstraintViolation> getViolations()
   {
      ...
   }
   
   public List<RESTEasyConstraintViolation> getFieldViolations()
   {
      return fieldViolations;
   }
   
   public List<RESTEasyConstraintViolation> getPropertyViolations()
   {
      return propertyViolations;
   }
   
   public List<RESTEasyConstraintViolation> getClassViolations()
   {
      return classViolations;
   }
   
   public List<RESTEasyConstraintViolation> getParameterViolations()
   {
      return parameterViolations;
   }
   
   public List<RESTEasyConstraintViolation> getReturnValueViolations()
   {
      return returnValueViolations;
   }
   
   public int size()
   {
      return getViolations().size();
   }
   
   public List<List<RESTEasyConstraintViolation>> getViolationLists()
   {
      return violationLists;
   }
   
   public String toString()
   {
      StringBuffer sb = new StringBuffer();
      for (Iterator<List<RESTEasyConstraintViolation>> it = violationLists.iterator(); it.hasNext(); )
      {
         List<RESTEasyConstraintViolation> violations = it.next();
         for (Iterator<RESTEasyConstraintViolation> it2 = violations.iterator(); it2.hasNext(); )
         {
            sb.append(it2.next().toString()).append('\r');
         }
      }
      return sb.toString();
   }

where org.jboss.resteasy.api.validation.RESTEasyConstraintViolation is defined:

public class RESTEasyConstraintViolation implements Serializable
{
   ...
   
   /**
    * @return type of constraint
    */
   public ConstraintType.Type getConstraintType()
   {
      return constraintType;
   }
   
   /**
    * @return description of element violating constraint
    */
   public String getPath()
   {
      return path;
   }
   
   /**
    * @return description of constraint violation
    */
   public String getMessage()
   {
      return message;
   }
   
   /**
    * @return object in violation of constraint
    */
   public String getValue()
   {
      return value;
   }
   
   /**
    * @return String representation of violation
    */
   public String toString()
   {
      return type() + "| " + path + "| " + message + "| " + value;
   }
   
   /**
    * @return String form of violation type 
    */
   public String type()
   {
      return constraintType.toString();
   }
}

and org.jboss.resteasy.api.validation.ConstraintType is the enumeration

public class ConstraintType
{
   public enum Type {CLASS, FIELD, PROPERTY, PARAMETER, RETURN_VALUE};
}

For example, RESTEasyViolationException.toString() might return something like

FIELD| s| size must be between 2 and 4| a
PROPERTY| t| size must be between 3 and 5| z
CLASS| | Concatenation of s and t must have length > 5| org.jboss.resteasy.validation.TestResource@68467a6f

where the fourth field is the violating value.

If the validation process throws a javax.validation.ValidationException, due to, for example, an improperly defined constraint annotation, RESTEasyViolationException.getException() will return it.

The form of validation mandated by the JAX-RS 2.0 specification, based on Bean Validation 1.1, is supported by the RESTEasy module resteasy-validator-provider-11, which produces the artifact resteasy-validator-provider-11-<version>.jar. Validation is turned on by default, though parameter and return value validation can be turned off or modified in the validation.xml configuration file. See the Hibernate Validator documentation for the details. Wildfly 8.x will ship with Hibernate Validator 5.x.

Validation is not included in the original JAX-RS specification, but RESTEasy 2.x provides a form of validation, including parameter and return value validation, based on Bean Validation 1.0 plus Hibernate 4.x extensions. For applications running in the context of Hibernate Validation 4.x, RESTEasy 3.x inherits the validation semantics from RESTEasy 2.x. This version of validation is in the RESTEasy module resteasy-hibernatevalidate-provider, which produces the artifact resteasy-hibernatevalidator-provider-<version>.jar. It follows the validation sequence given in the first section, detecting field, property, class, parameter, and return value constraints, though with a somewhat less rich semantics than resteasy-validator-provider-11.

Unlike resteasy-validator-provider-11, resteasy-hibernatevalidate-provider does not do validation testing by default. Validation must be turned on. There are two relevent annotations - org.jboss.resteasy.plugins.validation.hibernate.ValidateRequest and org.jboss.resteasy.plugins.validation.hibernate.DoNotValidateRequest - that are used to indicate what needs validation or not. We can tell RESTEasy to validate any method in a resource annotating the resource:

@Path("resourcePath")
@ValidateRequest
public interface Resource {
   
   @POST
   @Path("insert")
   public String insert(...

   @GET
   @Path("list")
   public String list(...
    
}

We can tell it to validate just some methods in an interface:

@Path("resourcePath")
public interface Resource {
   
   @POST
   @Path("insert")
   @ValidateRequest
   public String insert(...

   @GET
   @Path("list")
   public String list(...
    
}

This way RESTEasy will only trigger validation in insert method. It's possible to say what methods you don't want to be validated:

@Path("resourcePath")
@ValidateRequest
public interface Resource {
   
   @POST
   @Path("insert")
   public String insert(...
   
   @GET
   @Path("list")
   @DoNotValidateRequest
   public String list(...
    
}

RESTEasy obtains a bean validation implemenation by looking in the available META-INF/services/javax.ws.rs.Providers files for an implementation of ContextResolver<GeneralValidator>, where org.jboss.resteasy.spi.GeneralValidator is

public interface GeneralValidator
{
   /**
    * Validates all constraints on {@code object}.
    *
    * @param object object to validate
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return constraint violations or an empty set if none
    * @throws IllegalArgumentException if object is {@code null}
    *         or if {@code null} is passed to the varargs groups
    * @throws ValidationException if a non recoverable error happens
    *         during the validation process
    */
   public abstract <T> Set<RESTEasyConstraintViolation> validate(T object, Class<?>... groups);

    /**
    * Validates all constraints placed on the property of {@code object}
    * named {@code propertyName}.
    *
    * @param object object to validate
    * @param propertyName property to validate (i.e. field and getter constraints)
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return constraint violations or an empty set if none
    * @throws IllegalArgumentException if {@code object} is {@code null},
    *         if {@code propertyName} is {@code null}, empty or not a valid object property
    *         or if {@code null} is passed to the varargs groups
    * @throws ValidationException if a non recoverable error happens
    *         during the validation process
    */
   public abstract <T> Set<RESTEasyConstraintViolation> validateProperty(T object,
         String propertyName, Class<?>... groups);

     /**
    * Validates all constraints placed on the property named {@code propertyName}
    * of the class {@code beanType} would the property value be {@code value}.
    * <p/>
    * {@link ConstraintViolation} objects return {@code null} for
    * {@link ConstraintViolation#getRootBean()} and {@link ConstraintViolation#getLeafBean()}.
    *
    * @param beanType the bean type
    * @param propertyName property to validate
    * @param value property value to validate
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default}).
    * @return constraint violations or an empty set if none
    * @throws IllegalArgumentException if {@code beanType} is {@code null},
    *         if {@code propertyName} is {@code null}, empty or not a valid object property
    *         or if {@code null} is passed to the varargs groups
    * @throws ValidationException if a non recoverable error happens
    *         during the validation process
    */
   public abstract <T> Set<RESTEasyConstraintViolation> validateValue(
         Class<T> beanType, String propertyName, Object value,
         Class<?>... groups);

    /**
    * Returns the descriptor object describing bean constraints.
    * <p/>
    * The returned object (and associated objects including
    * {@link ConstraintDescriptor}s) are immutable.
    *
    * @param clazz class or interface type evaluated
    * @return the bean descriptor for the specified class
    * @throws IllegalArgumentException if clazz is {@code null}
    * @throws ValidationException if a non recoverable error happens
    *         during the metadata discovery or if some
    *         constraints are invalid.
    */
   public abstract BeanDescriptor getConstraintsForClass(Class<?> clazz);

    /**
    * Returns an instance of the specified type allowing access to
    * provider-specific APIs.
    * <p/>
    * If the Bean Validation provider implementation does not support
    * the specified class, {@link ValidationException} is thrown.
    *
    * @param type the class of the object to be returned
    * @return an instance of the specified class
    * @throws ValidationException if the provider does not support the call
    */
   public abstract <T> T unwrap(Class<T> type);

   /**
    * Validates all constraints placed on the parameters of the given method.
    *
    * @param <T> the type hosting the method to validate
    * @param object the object on which the method to validate is invoked
    * @param method the method for which the parameter constraints is validated
    * @param parameterValues the values provided by the caller for the given method's
    *        parameters
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return a set with the constraint violations caused by this validation;
    *         will be empty if no error occurs, but never {@code null}
    * @throws IllegalArgumentException if {@code null} is passed for any of the parameters
    *         or if parameters don't match with each other
    * @throws ValidationException if a non recoverable error happens during the
    *         validation process
    */
   public abstract <T> Set<RESTEasyConstraintViolation> validateAllParameters(
         T object, Method method, Object[] parameterValues,
         Class<?>... groups);

   /**
    * Validates all return value constraints of the given method.
    *
    * @param <T> the type hosting the method to validate
    * @param object the object on which the method to validate is invoked
    * @param method the method for which the return value constraints is validated
    * @param returnValue the value returned by the given method
    * @param groups the group or list of groups targeted for validation (defaults to
    *        {@link Default})
    * @return a set with the constraint violations caused by this validation;
    *         will be empty if no error occurs, but never {@code null}
    * @throws IllegalArgumentException if {@code null} is passed for any of the object,
    *         method or groups parameters or if parameters don't match with each other
    * @throws ValidationException if a non recoverable error happens during the
    *         validation process
    */
   public abstract <T> Set<RESTEasyConstraintViolation> validateReturnValue(
         T object, Method method, Object returnValue, Class<?>... groups);

   /**
    * Indicates if validation is turned on for a class.
    * 
    * @param clazz Class to be examined
    * @return true if and only if validation is turned on for clazz
    */
   public abstract boolean isValidatable(Class<?> clazz);
     
   /**
    * Indicates if validation is turned on for a method.
    * 
    * @param method method to be examined
    * @return true if and only if validation is turned on for method
    */   
   public abstract boolean isMethodValidatable(Method method);
}

The methods and the javadoc are adapted from the Bean Validation 1.1 classes javax.validation.Validator and javax.validation.executable.ExecutableValidator.

RESTEasy supplies two implementations of GeneralValidator, in the modules resteasy-validator-provider-11 and resteasy-hibernatevalidator-provider. An alternative implementation may be supplied by implementing ContextResolver<GeneralValidator> and org.jboss.resteasy.spi.validation.GeneralValidator.