Spring MVC: Controller level model binding validation
Tags: spring, springmvc
Published: 2024-01-31 21:25:45

## Notice

I wrote this article and was originally published on [Qiita](https://qiita.com/saladlam/items/1609c60889223117e1c0) on 9 July 2021.

---

To perform model binding validation of controller, one of the solution is register validator on controller.

#1. Write your own validator

You validator should extend *org.springframework.validation.Validator* interface. Below is a example.

```java
public class MessageDtoValidator implements Validator {

	@Override
	public boolean supports(Class<?> clazz) {
		return MessageDto.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		// target is model being checked and errors register into errors instance
		ValidationUtils.rejectIfEmpty(errors, "publishDate", "publishDate.empty");
		ValidationUtils.rejectIfEmpty(errors, "description", "description.empty");
	}

}
```

#2. Register your validator

You should register your validator on function annotated with *org.springframework.web.bind.annotation.InitBinder* annotation with *org.springframework.validation.DataBinder.setValidator(Validator)* function.

```java
// Call everytime when recevies request
@InitBinder
public void initBinder(WebDataBinder binder) {
	...
	binder.setValidator(new MessageDtoValidator())
	...
}
```

*org.springframework.validation.Validator.supports(Class<?>)* of your validator will be called when calling *setValidator()* to verify is your validator supports model binded.

#3. Specify model argument you want to verify and define corresponding action

You should add *javax.validation.Valid* (or *org.springframework.validation.annotation.Validated*) annotation in addition with *org.springframework.web.bind.annotation.ModelAttribute* annotation on model argument you want to verify. And specify actions when binding error is found.

```java
@PostMapping("/{messageId}/save")
public String saveEditMessage(@PathVariable("messageId") long id, @Valid @ModelAttribute MessageDto message, BindingResult errors) {
	if (errors.hasErrors()) {
		// your action
		...
	}
}
```

After binding model, framework will check if validation annotation exists on model argument by *org.springframework.web.method.annotation.ModelAttributeMethodProcessor.determineValidationHints(Annotation)* function.

```java
/**
 * Determine any validation triggered by the given annotation.
 * @param ann the annotation (potentially a validation annotation)
 * @return the validation hints to apply (possibly an empty array),
 * or {@code null} if this annotation does not trigger any validation
 * @since 5.1
 */
@Nullable
private Object[] determineValidationHints(Annotation ann) {
	Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
	if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
		Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
		if (hints == null) {
			return new Object[0];
		}
		return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
	}
	return null;
}
```

And then actual validation logic *org.springframework.validation.Validator.validate(Object, Errors)* will be called by *org.springframework.web.method.annotation.ModelAttributeMethodProcessor.validateIfApplicable(WebDataBinder, MethodParameter)* function.

#Example

You can find actual implementation from my [notice board example application](https://github.com/saladlam/spring-noticeboard).