Jersey Bean validation of REST request / query parameters using standard annotation (example) – I

By | December 5, 2015
  • In our web application, we generally expose the APIs to be invoked by clients, So there is always the requirement for validation of query or request parameters, path parameter, header parameters etc. at the server.
  • We generally need to validate the request or query params before the request being serviced by REST resource.
  • We will use jersey bean validation to validate the request params.

Example – Apply validation to RESTful web service.

  • Suppose new student join the university and we would like to register new student. so we will follow the following procedure to register new student.
  • Admin staff collect the attribute of student like
    • First Name
    • Last Name
    • Age
    • Email address
    • Phone number
  • Admin staff fill the details of Student in their portal (Web Client).
  • Register the Student (Clicking some button like register, enroll etc.).
  • Request will reach the web service.
  • Web Service validate each parameters like firstName, age, email etc.
  • Web Service update the database
  • Web service send the response back to client (about success or failure)

We will validate the request parameters of RESTFul web service using jersey bean validation. We have already discussed to create RESTful web service using jersey framework. We will use following annotations provided by Jersey & java framework.
@DecimalMin, @Size, @Pattern, @Min etc.

  • We will use constraint annotations at resource & bean class.
  • We have discussed the custom validation annotation  in Validation of REST Parameter using Jersey Framework
  • We will create will write our own validation class to validate the request parameter. In this post we will use the validations provided by frameworks only.

Example of validation in REST resource

 @GET
 @Path("/validate")
 public Response validateStudent(
    @Size(min = 2, max = 25, message = "firstName Length should be between 2 and 25")
    @QueryParam("firstName") String firstName,
    @Size(min = 2, max = 25, message = "lastName Length should be between 2 and 25")
    @QueryParam("lastName") String lastName,

    @Min(value=15, message = "age should not be less that 15")
    @QueryParam("age")
    String age) throws ValidationException {

In “/validate” request (we will show sooner), we are validating “firstName” query param, we are ensuring that it should have at least 2 character and at max it can have 25 character. Besides, we are specifying the error message that will be generated once there is any violation e.g. if firstName have length less that 2 then exception message of “firstName Length should be between 2 and 25 character” will be sent back.

Jersey Maven dependencies (bean validation)


<dependency>
	<groupId>org.glassfish.jersey.containers</groupId>
	<artifactId>jersey-container-servlet</artifactId>
	<version>${jersey_version}</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.media</groupId>
	<artifactId>jersey-media-json-jackson</artifactId>
	<version>${jersey_version}</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.ext</groupId>
	<artifactId>jersey-bean-validation</artifactId>
	<version>${jersey_version}</version>
</dependency>

Program – validation of REST request / query parameters (standard annotations)

1.) StudentResource class:

  • The student resource will expose the REST APIs, which will be invoked by its REST client.
  • The annotation itself explain everything, like @Size annotation is specify min and max size of input request.
package org.learn.resource;

import org.learn.model.Student;

import javax.validation.Valid;
import javax.validation.ValidationException;
import javax.validation.constraints.Min;
import javax.validation.constraints.Size;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Date;


@Path("/student")
public class StudentResource {

    private static final String text = "Message from Server :\n%s";

    @GET
    @Consumes(MediaType.TEXT_PLAIN)
    public Response registerStudent() {
        String response = String.format(text, new Date());
        return Response.status(Response.Status.OK).entity(response).type(MediaType.TEXT_PLAIN).build();
    }

    @GET
    @Path("/validate")
    public Response validateStudent(
            @Size(min = 2, max = 25, message = "firstName Length should be between 2 and 25 character")
            @QueryParam("firstName") String firstName,

            @Size(min = 2, max = 25, message = "lastName Length should be between 2 and 25 character")
            @QueryParam("lastName") String lastName,

            @Min(value = 15, message = "age should not be less that 15")
            @QueryParam("age")
                    String age) throws ValidationException {

        String student = String.format("firstName: %s, lastName: %s, age: %s", firstName, lastName, age);
        String response = String.format(text, student);
        return Response.status(Response.Status.OK).entity(response).type(MediaType.TEXT_PLAIN).build();
    }

    @POST
    @Path("/register")
    public Response registerStudent(
            @Valid Student student) throws ValidationException {

        String response = String.format(text, student);
        return Response.status(Response.Status.OK).entity(response).type(MediaType.TEXT_PLAIN).build();
    }
}

Suppose client used API “host:port:/student/valiadte” to post the request to our service. We have deployed our application at root, so do not have any context path in our request url.

The request response for couple of APIs are

  1. Requet API  – /student/validate:
    http://localhost:9090/student/validate?firstName=don&lastName=brad&age=16
    Response from service:
    Message from Server :
    firstName: don, lastName: brad, age: 16
  2. Request API  – /student/register :
    http://localhost:9090/student/register
    request raised from client:
    {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don.bradman@ymail.com”,”age”:”18″}

2.) Student class:

  • Sudent is model class, we have used Student class in /register api.
  1. The client raise the JSON request like
    {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don.bradman@ymail.com”,”age”:”18″}
  2. We have defined @DecimalMin annotation which specify the minimum age of student.
  3. In our input we have specified age as 18 which is within the min and max defined on age data member.
  4. Also, we have used @Pattern annotation to validate the email address.
    • This validation will validate the “don.bradman@ymail.com” email of incoming object.
    • If there is any violations in input parameters, then ValidationException will be raised.
  5. We have defined ValidationException mapper class to catch all validation exceptions.
package org.learn.model;

import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "student")
public class Student {

    @DecimalMin(value = "15", message = "Student shall be minimum of age 15 yr")
    @DecimalMax(value = "30", message = "Student can not have age more than 30 yr")
    private int age;

    @Size(min = 1, max = 25, message = "The length of firstName should be between 1 to 25")
    private String firstName;

    @Size(min = 1, max = 25, message = "The length of firstName should be between 1 to 25")
    private String lastName;

    @Pattern(message = "Invalid Email Address->" +
            "Valid emails:user@gmail.com or my.user@domain.com etc.",
            regexp = "^[a-zA-Z0-9_!#$%&�*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$")
    private String email;


    public String getEmail() {
        return email;
    }

    public int getAge() {
        return age;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Student() {

    }

    public String toString() {
        return String.format("firstName : %s, lastName: %s, email : %s, age: %s",
                firstName, lastName, email, age);
    }
}

3.) ValidationExceptionMapper class:

  • Validation exception raised if there is any constraint violation request parameter like invalid age. ValidationExceptionMapper  will catchc validation exception.
  • Jersey bean validation jar (check project pom file) has also defined the validation exception mapper, so If we would like to catch validation exceptions, then we need to define the explicit exception mapper for validation exception.
  • Suppose client used API “:port://student/register” and send some invalid request.

Examples of Validation Exceptions:

  1. The json used to send to call the api. As we do not have firstName.
    {“firstName”:””,”lastName”:”last”,”email”:”lovetojava@ymail.com”,”age”:”15″}
    Exception occurred:
    registerStudent.arg0.firstName The length of firstName should be between 1 to 25
  2. Invalid Email Address
    {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don bradman@ymail.com”,”age”:”18″}
    Exception occurred :
    registerStudent.arg0.email Invalid Email Address->Valid emails:user@gmail.com or my.user@domain.com etc.Now let us take at cod of ValidationException
package org.learn.exception;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class ValidationExceptionMapper implements ExceptionMapper<javax.validation.ValidationException> {

    @Override
    public Response toResponse(javax.validation.ValidationException e) {
        final StringBuilder strBuilder = new StringBuilder();
        for (ConstraintViolation<?> cv : ((ConstraintViolationException) e).getConstraintViolations()) {
            strBuilder.append(cv.getPropertyPath().toString() + " " + cv.getMessage());
        }
        return Response
                .status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
                .type(MediaType.TEXT_PLAIN)
                .entity(strBuilder.toString())
                .build();
    }
}

4.) ApplicationExceptionMapper class:

  • ApplicationExceptionMapper class is used to catch all applications exceptions.
package org.learn.exception;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class ApplicationExceptionMapper implements ExceptionMapper<Exception> {

    public Response toResponse(Exception e) {
        return Response
                .status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
                .type(MediaType.TEXT_PLAIN)
                .entity(e.getMessage())
                .build();
    }
}

Output –  Jersey bean validation for REST request/query parameters

 

    1. Requests for http://localhost:9090/student/validate
      Request: GET /student/validate
      http://localhost:9090/student/validate?firstName=don&lastName=brad&age=16
      Response:
      Message from Server :
      firstName: don, lastName: brad, age: 16
    2. Request: GET /student/validate
      http://localhost:9090/student/validate?firstName=&lastName=brad&age=16
      Response:
      registerStudent.arg0 firstName Length should be between 2 and 25 character
    3. Request – We have provided the invalid age, so we will get exception message from server
      http://localhost:9090/student/validate?firstName=don&lastName=brad&age=10
      Response:
      registerStudent.arg2 age should not be less that 15
    4. http://localhost:9090/student/register
      Request: POST /student/register
      {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don.bradman@ymail.com”,”age”:”18″}
      Response:
      Message from Server :
      firstName : Don, lastName: bradman, email : don.bradman@ymail.com, age: 18
    5. Request: POST /student/register
      {“firstName”:””,”lastName”:”last”,”email”:”lovetoyogesh@ymail.com”,”age”:”15″}
      Response:
      registerStudent.arg0.firstName The length of firstName should be between 1 to 25
    6. Request: POST /student/register
      {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don bradman@ymail.com”,”age”:”18″}
      Response:
      registerStudent.arg0.email Invalid Email Address->Valid emails:user@gmail.com or
      my.user@domain.com etc.
    7. Request: POST /student/register
      {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don.bradman@ymail.com”,”age”:”14″}
      Response:
      registerStudent.arg0.age Student shall be minimum of age 15 yr
    8. Request: POST /student/register
      {“firstName”:”Don”,”lastName”:”bradman”,”email”:”don.bradman@ymail.com”,”age”:”40″}
      Response:
      registerStudent.arg0.age Student can not have age more than 30 yr

 

Download code – Jersey bean validation RESTFul web service