Introducing validations in JAX-RS applications

 

Introducing validations in JAX-RS applications

Validation is the process of ensuring the completeness and sanity of business data before posting it to the underlying data source. Validating the client input is very important for any REST API that you build. In this section, we will see the offering in JAX-RS for validating client input.

The JAX-RS 2.x release allows you to validate the resource class and methods via Java Bean Validation. This framework is a Java specification, which lets you specify the validation rules on the objects or hierarchy of objects via the built-in annotations; it also lets you write the reusable validation rules for handling various use cases. Bean Validation is integrated into the Java EE platform, allowing developers to easily define and enforce the validation rules across various components that come as a part of Java EE. The following is a list of Java EE components that support Bean Validation: JAX-RSJAXBJPAEJBJSF, and CDI.

The latest release of Bean Validation is Version 2.0, which is based on JSR 380 and formerly JSR 349. You can learn more about JSR 380 at http://beanvalidation.org/2.0/spec/#introduction.


A brief introduction to Bean Validation

The Bean Validation framework allows you to specify the validation rules in the form of annotations on a field, method, or a Java class. You can either use built-in constraints offered by Bean Validation or build custom constraints by using the extension mechanisms offered by the framework. The following table summarizes the built-in constraints offered by Bean Validation that are applicable to all data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@NotNull

Applicable to all Java types

The annotated variable must not be null.

@Null

Applicable to all Java types

The annotated variable must be null.

The following table summarizes the built-in constraints offered by Bean Validation that are applicable to the Boolean data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@AssertFalse

  • java.lang.Boolean
  • Boolean

The annotated variable must be false.

@AssertTrue

  • java.lang.Boolean
  • Boolean

The annotated variable must be true.

The following table summarizes the built-in constraints offered by Bean Validation that are applicable to the number data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@DecimalMax

  • java.math.BigDecimal
  • java.math.BigInteger
  • java.lang.String
  • byteshortintlong, and their wrapper types

The annotated variable must not exceed the maximum specified limit.

@DecimalMin

  • java.math.BigDecimal
  • java.math.BigInteger
  • java.lang.String
  • byteshortintlong, and their wrapper types

The annotated variable must be higher than or equal to the minimum specified value.

@Digits

  • java.math.BigDecimal
  • java.math.BigInteger
  • java.lang.String
  • byteshortintlong, and their wrapper types

The annotated variable must be a number within the acceptable range.

@Max

  • java.math.BigDecimal
  • java.math.BigInteger
  • byteshortintlong, and their wrapper types

The annotated variable must be a number with a value less than or equal to the specified maximum value.

@Min

  • java.math.BigDecimal
  • java.math.BigInteger
  • byteshortintlong, and their wrapper types

The annotated variable must be a number with a value larger than or equal to the specified minimum value.

The following table summarizes the built-in constraints offered by Bean Validation that are applicable to the date data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@Future

  • java.util.Date
  • java.util.Calendar

The annotated variable must be a date in the future.

@Past

  • java.util.Date
  • java.util.Calendar

The annotated variable must be a date in the past.

 

The following table summarizes the built-in constraints offered by Bean Validation that are applicable to the string data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@Pattern

java.lang.String

The annotated string must match the regular expression specified.

 

The following table summarizes the built-in constraints offered by Bean Validation that are applicable to the collection and string data types:

Validation constraints

Supported Java types

Details of the imposed constraints

@Size

  • java.lang.String(string length is evaluated)
  • jav.util.Collection(collection size is evaluated)
  • java.util.Map (map size is evaluated)
  • * java.lang.Array (array length is evaluated)

The annotated string or collection size must be between the specified boundaries (included).

The following code snippet illustrates the use of built-in validation constraints provided by Bean Validation for validating an input to a JAX-RS method call. This example validates the input parameter for a positive number:

@GET 
@Path("departments/{id}") 
@Produces(MediaType.APPLICATION_JSON) 
public Department findDepartment(@PathParam("id") 
    @Min(value = 0, message = "Department Id must be a  
    positive value")  
    Short id, @Context Request request) { 
 
    Department department = findDepartmentEntity(id); 
    return department; 
} 

Building custom validation constraints

In the previous section, we had a quick look at the built-in validation constraints offered by the Bean Validation framework. There may be cases where you may want to build your own validation constraints. The Bean Validation framework supports that as well.

The core contracts that you should implement for a custom validation constraint are as follows:

  • Annotation for the custom validation constraint
  • Implementation of the javax.validation.ConstraintValidatorContext interface. This class contains your custom validation logic
  • Definition of a default error message

Let's build a custom validation constraint for checking whether a department with the same name already exists for a given location. This constraint avoids the duplicate department entities.

The first step is to build an annotation for the custom constraint that you are going to construct. The Bean Validation framework specification demands the following attribute definitions in a constraint annotation:

  • message: This attribute returns the default key for reading the validation error messages.
  • groups: This attribute specifies the validation groups to which this constraint belongs to. This is default to an empty array of the Class<?> type.
  • payload: This attribute can be used by clients for assigning custom payload objects to a constraint. This attribute is not used here.

Let's define an annotation for the custom validation constraint, ValidDepartmentValidator, which we are going to build next:


//Other import statements are omitted for brevity 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.Target; 
import java.lang.annotation.RetentionPolicy; 
import javax.validation.Constraint; 
import javax.validation.Payload;  
 
//@Constraint links a constraint annotation with  
//its constraint validation implementations. 
@Constraint(validatedBy = {ValidDepartmentValidator.class}) 
@Target({ElementType.FIELD, ElementType.PARAMETER}) 
@Retention(value = RetentionPolicy.RUNTIME) 
public @interface ValidDepartment { 
 
    //The message key for validation error message  
    //fails the validation. 
    String message() default  
    "{com.packtpub.rest.validation.deptrule}"; 
    Class<?>[] groups() default {}; 
    Class<? extends Payload>[] payload() default {}; 
} 

As the next step, we need to provide the implementation for the custom validation constraint called ValidDepartmentValidator. Here is the code snippet:

//Other import statements are omitted for brevity 
import javax.validation.ConstraintValidator; 
import javax.validation.ConstraintValidatorContext; 
 
//Defines the logic to validate the constraint 'ValidDepartment' 
//for the object type 'Department' 
public class ValidDepartmentValidator implements 
    ConstraintValidator<ValidDepartment, Department> { 
 
    @Override 
    public void initialize(ValidDepartment constraintAnnotation) { 
    } 
 
    @Override 
    public boolean isValid(Department department,  
        ConstraintValidatorContext context) { 
        //Implementation of isDeptExistsForLoc() is not shown here  
        //to save space. This method return true if the given 
        // department id is found in the database 
        if(isDeptExistsForLoc(department.getDepartmentId(), 
            department.getDepartmentName(),  
            department.getLocationId())) { 
            return false; 
 
        } 
 
        return true; 
    } 
    //Rest of the code goes here 
 
} 

The last step is to define the error message in a resource bundle in order to localize the validation error messages. This file should be added to the root folder with the following name: ValidationMessages.properties. The file contents may look like the following code:

com.packtpub.rest.validation.deptrule= Department already exists  

This feature lets you localize the validation error messages. For example, ValidationMessages_fr.properties contains messages for the French locale. The custom validation constraint for checking a duplicate department entity is ready for use now!

You can apply this constraint on the method parameters or class fields, as shown in the following code snippet:

@POST 
@Path("departments") 
@Consumes(MediaType.APPLICATION_JSON) 
public void create(@ValidDepartment Department entity) { 
    createDepartmentEntity(entity); 
} 

What happens when Bean Validation fails in a JAX-RS application?

The framework throws javax.validation.ValidationException or its subclass exceptions, such as javax.validation.ConstraintViolationException, when the Bean Validation rules that you set on the model class fails at runtime. The JAX-RS runtime will report the validation error back to the client with a 500 (internal server error) HTTP status code. You can override the default validation exception handling by providing a custom javax.ws.rs.ext.ExceptionMapper class. We discussed ExceptionMapper a while ago in the Mapping exceptions to the response message using ExceptionMapper section.

























































































































































Comments

Popular posts from this blog

Understanding the JAX-RS resource life cycle

Generating a chunked output using Jersey APIs