HibernateValidator

Installation with Maven

Add the following packages:

Installation with Spring Boot

Add only this dependency:

Validating constraints

📖 Reference guide - Chapter 2

Defining constraints

Example

package org.hibernate.validator.referenceguide.chapter01;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

public class Car {

    @NotNull
    private String manufacturer;

    @NotNull
    @Size(min = 2, max = 14)
    private String licensePlate;

    @Min(2)
    private int seatCount;

    public Car(String manufacturer, String licencePlate, int seatCount) {
        this.manufacturer = manufacturer;
        this.licensePlate = licencePlate;
        this.seatCount = seatCount;
    }

    //getters and setters ...
}


package org.hibernate.validator.referenceguide.chapter01;

import java.util.Set;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;

import org.junit.BeforeClass;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class CarTest {

    private static Validator validator;

    @BeforeClass
    public static void setUpValidator() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    @Test
    public void manufacturerIsNull() {
        Car car = new Car( null, "DD-AB-123", 4 );

        Set<ConstraintViolation<Car>> constraintViolations =
                validator.validate( car );

        assertEquals( 1, constraintViolations.size() );
        assertEquals( "must not be null", constraintViolations.iterator().next().getMessage() );
    }

    @Test
    public void licensePlateTooShort() {
        Car car = new Car( "Morris", "D", 4 );

        Set<ConstraintViolation<Car>> constraintViolations =
                validator.validate( car );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "size must be between 2 and 14",
                constraintViolations.iterator().next().getMessage()
        );
    }

    @Test
    public void seatCountTooLow() {
        Car car = new Car( "Morris", "DD-AB-123", 1 );

        Set<ConstraintViolation<Car>> constraintViolations =
                validator.validate( car );

        assertEquals( 1, constraintViolations.size() );
        assertEquals(
                "must be greater than or equal to 2",
                constraintViolations.iterator().next().getMessage()
        );
    }

    @Test
    public void carIsValid() {
        Car car = new Car( "Morris", "DD-AB-123", 2 );

        Set<ConstraintViolation<Car>> constraintViolations =
                validator.validate( car );

        assertEquals( 0, constraintViolations.size() );
    }
}

Utilities

Types of constraints

  • Field-level
  • Property-level
  • Container element constraints -> you can specify constraints directly on the type argument of a parameterizer type
    • with Iterable
    • with List
    • with Set
    • with Map
    • with java.util.Optional
    • with custom container types
    • with object graphs

Constraint bean with objet graphs

Let Car be:

package org.hibernate.validator.referenceguide.chapter02.objectgraph;

public class Car {

    @NotNull
    @Valid
    private Person driver;

    //...
}

driver refer to Person defined here:

package org.hibernate.validator.referenceguide.chapter02.objectgraph;

public class Person {

    @NotNull
    private String name;

    //...
}

Suppose i have this code:

private static Validato validator = Validation.buildDefaultValidatorFactory().getValidator();

Car car = new Car(new Person(/*..*/), /*...*/);
validator.validate(car)

If i validate an instance of Car, even the Person associated with that instance will be validated.

ConstraintViolation class

Set<ConstraintViolation<Boardgame>> constraintViolations = validator.validate(boardgame); //May contain lot of violations

if (constraintViolations.size() > 0){ //if there are any violatins
	//iterate over violations
	for (ConstraintViolation<Boardgame> cv : constraintViolations){ 
		//print messages
		System.out.println("Invalid input for class: " + cv.getRootBeanClass());
		System.out.println("Invalid value: " + cv.getInvalidValue() + " triggered message error: " + cv.getMessage());
	}
}

Extending the library by implementing custom annotations

  • ❔What alternatives are there to Hibernate Validator’s @SafeHtml to Validate Strings?

  • I followed everything written in this blog post except that i’m using Jsoup instead of Html Sanitizer

  • The @NoHtml bean should protect against most XSS injections.

    1. Define an annotation to be applied to an Entity’s field. This annotation will be linked to a validation implementation.
    2. Define a custom validation constraint to do the actual safety check, we can leverage the OWASP Java HTML Sanitizer. The sanitizer will sanitize the string, and the validation check will just be to see that the sanitized version is the same as the original version (thus showing that it does not contain content against your security policy).
    3. Apply the validation annotation to the Entity’s field.
    4. Apply the @Valid annotation to the Controller argument, include the BindingResult in the Controller method, and check it inside the method for finer control over sanitized input failures
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = NoHtmlValidator.class)
@Documented
public @interface NoHtml {
    // TODO use a better message, look up ValidationMEssages.properties
    String message() default "{org.myproject.constraints.nohtml}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

NoHtml is valiedated from NoHtmlValidator. In this one class you can define whanver logic you want

public class NoHtmlValidator implements ConstraintValidator<NoHtml, String> {

   @Override
   public boolean isValid(String value, ConstraintValidatorContext context)
   {
      return value == null || Jsoup.isValid(value, Safelist.basic());
   }
}

My Boardgame class:

@Entity
@Table(name = "Boardgame")
public class Boardgame {

	@NotNull
	@Id
	private String name;

	@DecimalMin("0.0")
	@Column
	private float price = 0.0f;

	@Min(0)
	@Column
	private int quantity = 0;

	@NotNull
	@Size(max = 1025)
	@Column
	@NoHtml // <---------------------- HERE
	private String description = "";

	//Constructors
	//Getter
	//Setters
}

Value extraction

Value extraction is the process of extracting values from a container so that they can be validated. It is used when dealing with container element constraints and cascaded validation inside containers.

Two example of containers are java.util.List and java.util.Optional

A more interesting example is the case of Guava’s Multimap: we would like to be able to validate both the keys and the values of this container type.

To make something like this: private Multimap<@NotBlank String, @NotBlank String> map2;

You need to define two ValueExtractor. One for values:

package org.hibernate.validator.referenceguide.chapter07.valueextractor;

public class MultimapValueValueExtractor
        implements ValueExtractor<Multimap<?, @ExtractedValue ?>> {

    @Override
    public void extractValues(Multimap<?, ?> originalValue, ValueReceiver receiver) {
        for ( Entry<?, ?> entry : originalValue.entries() ) {
            receiver.keyedValue( "<multimap value>", entry.getKey(), entry.getValue() );
        }
    }
}

And one for keys:

package org.hibernate.validator.referenceguide.chapter07.valueextractor;

public class MultimapKeyValueExtractor
        implements ValueExtractor<Multimap<@ExtractedValue ?, ?>> {

    @Override
    public void extractValues(Multimap<?, ?> originalValue, ValueReceiver receiver) {
        for ( Object key : originalValue.keySet() ) {
            receiver.keyedValue( "<multimap key>", key, key );
        }
    }
}

The next step is to choose the appropriate method from ValueReceiver between value(), iterableValue(), indexedValue() and keyedValue().

I valu Extractors possono essere definiti durante il Bootstrapping

Configuring via XML

Bootstrapping

In Section 2.2.1, “Obtaining a Validator instance”, you already saw one way of creating a Validator instance - via Validation#buildDefaultValidatorFactory(). In this chapter, you will learn how to use the other methods in jakarta.validation.Validation in order to bootstrap specifically configured validators.

The generated ValidatorFactory and Validator instances are thread-safe and can be cached. As Hibernate Validator uses the factory as context for caching constraint metadata it is recommended to work with one factory instance within an application.