When you are working on any somehow complex application, you need to enable validation. For example, if user submits a form or a request, your app should check, that data is in an appropriate format and required fields are not missed. When user signs up, we need to validate that an email address is proper and a password has sufficient length.

There are two types of validation: server-side and client-side. And while there are discussions on which is better, I think there is no space for discussions. You must implement both. While client-side validation is important as it can provide immediate result to users (especially, when you build SPAs with frameworks like Vue, server-side provides a centralized validation, which is common for all clients (web, mobile, desktop) as well handle requests, that are executed via direct HTTP calls aside client applications (for example, when you perform a request from curl).

Java has a number of reliable validation libraries, although they are not quite good for microservices. First and foremost, due to their size, speed and overall complexity. If you use Javalin to build services, you now that it has some validation capabilities. Although, they are not easy to use. Better to rely on an external solution. In this post I would like to demonstrate you a small tinyvalidator library, that can be used on handler level for validating inputs.

Do you want to increase your Java collections skills?

This topic is really huge, and a single post is not enough to cover all. That is why I wrote this Practical Guide. Everything you need in the one book. Do you want to become Java ninja?

Define a data model

Validation starts from a definition of required data and its parameters. tinyvalidator uses annotation-based approach. Let demonstrate this on a concrete example. I would like to take an application, that we have built in the post about Javalin JWT authentication. In this article we will do a validation for auth controller.

Take a look on the modified AuthRequest entity below:

public class AuthRequest {

    @Email
    private String email;

    @Size(min = 8)
    private String password;

    // getters, setters
}

In this code snippet we used two annotations: @Email and @Size. The first one checks, that a field contains a valid email address (although, this applies to format. You need to implement an email confirmation flow to check, that this email does exist). The second one asserts that the length of password string is not less than 8.

There is a number of other useful built-in validator annotations you may need in your API:

  • @NotNull – checks that a field is not empty
  • @HttpUrl – checks that a field contains a valid url
  • @Pattern – accepts a field, that matches a defined regular experession

Call a validator

The second part of building a validation flow is actually calling a validator component, that does assertion of a bean according to defined rules. Let take UserController, which handles HTTP requests/responses. First, we need to create a new validator instance:

Validator validator = new Validator();

Then we need to pass a bean into a validator. In our case a bean represents an login/signup request (AuthRequest entity):

List<ConstraintViolation> violations = validator.validate(request);

This method basically returns a collection of violations. If a bean is valid, this list will be empty. Otherwise, we will obtain missed fields with comments. In this case we need to break a handler flow with BadRequest response, followed by an error message:

if (!violations.isEmpty()) {
    String message = violations.stream()
        .map(v -> v.getName().concat(": ").concat(v.getMessage()))
        .collect(Collectors.joining(", "));
    throw new BadRequestResponse(message);
}

Otherwise, a handler execution should be continued. Take a look on a complete implementation of login() and signup() methods below:

public void login(Context context) {
    Validator validator = new Validator();
    AuthRequest request = context.bodyAsClass(AuthRequest.class);
    List<ConstraintViolation> violations = validator.validate(request);
    if (!violations.isEmpty()) {
        String message = violations.stream()
            .map(v -> v.getName().concat(": ").concat(v.getMessage()))
            .collect(Collectors.joining(", "));
        throw new BadRequestResponse(message);
    }
    AuthResponse result = service.login(request);
    context.json(result);
}

public void signup (Context context){
    AuthRequest request = context.bodyAsClass(AuthRequest.class);
    Validator validator = new Validator();
    List<ConstraintViolation> violations = validator.validate(request);
    if (!violations.isEmpty()) {
        String message = violations.stream()
            .map(v -> v.getName().concat(": ").concat(v.getMessage()))
            .collect(Collectors.joining(", "));
        throw new BadRequestResponse(message);
    }
    AuthResponse result = service.signup(request);
    context.json(result);
}

Test

The final step is a testing. Let perform API calls with a valid request and with an invalid one to check that a validation actually works.

First, call signup endpoint with an invalid email address and a short password (less than 8 symbols):

Graph 1. Bad request

Now, execute a same endpoint with a valid data:

Graph 2. Valid request

As you could see, the tinyvalidator library is a small and simple solution to perform a validation flow. It works fine with Javalin, as simple technologies are good together. You can find a complete source code for this post in this github repository. If you have questions regarding a server-side validation in Javalin drop a comment below or contact me.