Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Validating data

You cannot trust a client: it may send an empty title, a body that is too short or broken JSON. Validation checks input data before it reaches the store. Javalin has validators for this: the bodyValidator, queryParamAsClass, pathParamAsClass and headerAsClass methods return a Validator object to which you add rules.

How a validator works

  1. A ...AsClass method or bodyValidator creates a validator and tries to turn the string or JSON into the required type.
  2. check(condition, "message") methods add rules.
  3. The get() method returns the value or throws ValidationException if any rule is broken.

Javalin turns ValidationException into a 400 response with a list of errors in JSON by itself.

Checking the request body

Replace bodyAsClass in the post creation route with bodyValidator:

// src/main/java/com/example/blog/Main.java
config.routes.post("/posts", ctx -> {
    NewPost data = ctx.bodyValidator(NewPost.class)
            .check(p -> p.title() != null && !p.title().isBlank(), "Title is required")
            .check(p -> p.body() != null && p.body().length() >= 5, "Body is too short")
            .get();
    ctx.status(HttpStatus.CREATED).json(store.create(data));
});

Each rule receives the ready NewPost object and returns true if everything is fine. Let us send a wrong post:

curl -i -X POST http://localhost:8080/posts \
     -d '{"title":"","body":"abc","author":"Oleg"}'
HTTP/1.1 400 Bad Request
Content-Type: application/json

{"REQUEST_BODY":[{"message":"Title is required","args":{},"value":{"title":"","body":"abc","author":"Oleg"}},{"message":"Body is too short","args":{},"value":{"title":"","body":"abc","author":"Oleg"}}]}

Javalin checked all the rules and returned both errors at once. The REQUEST_BODY key means the error is in the request body.

Checking parameters

The same rules work with parameters. Let us limit the page size in search:

// src/main/java/com/example/blog/Main.java
int limit = ctx.queryParamAsClass("limit", Integer.class)
        .check(n -> n >= 1 && n <= 50, "Limit must be from 1 to 50")
        .getOrDefault(10);

For ?limit=100 the server answers 400 and {"limit":[{"message":"Limit must be from 1 to 50","args":{},"value":100}]}, and without the parameter it uses 10.

Methods for getting the value

MethodIf the value is missingIf a rule is broken
get()400, error NULLCHECK_FAILED400
getOrDefault(10)The default value400
getOrNull()null400

Common mistake

ctx.bodyAsClass does not check data: for broken JSON such as {"title":"x" without the closing brace, the client gets 500 Server Error, and the log shows JsonEOFException: Unexpected end-of-input. It looks like a server error, although the client made the mistake. With bodyValidator the same request gets the right answer:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{"REQUEST_BODY":[{"message":"DESERIALIZATION_FAILED","args":{},"value":"{\"title\":\"x\""}]}

Use bodyValidator for data that comes from a client.

Thanks for reading!

Donate to the site — we work hard for you.