Custom Javax Annotation Error Handling in Spring
Tags: spring, springboot, java, validation
Published: 2020-02-15 20:29:50

---
title: Custom Javax Annotation Error Handling in Spring
published: true
date: 2020-02-01 22:41:00 UTC
tags: spring,springboot,java,validation
canonical_url: http://kamerelciyar.com/custom-javax-annotation-error-handling/
---

Hello! In this article, I’m gonna cover how to override default exception handler method for `MethodArgumentNotValidException` which is thrown when Java Bean Validation Annotations are violated.<!--more-->

### What is Java Bean Validation?

Java Bean Validation is a specification that is defined in [JSR-380](%5Bhttps://beanvalidation.org/2.0/%5D(https://beanvalidation.org/2.0/)). It makes possible defining constraints with annotations, write custom constraints, etc. I will use [Hibernate Validator](http://hibernate.org/validator/) since it is the only certified implementation of Bean Validation 2.0. It’s not a requirement to use with Spring but I’m going to implement it with Spring Boot because of its popularity. I will not explain how to use these annotations however you can find it in [one of my articles](/validations-in-spring-boot/#javax-validation-constraints).

### Create a Simple Project and Provide Some Annotations

Create a project with Spring Initializr and select Web and Lombok dependencies. Because web-starter includes Hibernate Validator. Then create a class named User as below.

```java
@Data
public class User {  
  
    @Length(min = 2, max = 30, message = "Name must be between 2-30 characters. ")  
    @Pattern(regexp = "^[a-zA-Z]+(([',. -][a-zA-Z])?[a-zA-Z]*)*$", message = "Name is invalid.")  
    private String name;  
  
    @Length(min = 2, max = 30, message = "Surname must be between 2-30 characters.")  
    @Pattern(regexp = "^[a-zA-Z]+(([',. -][a-zA-Z])?[a-zA-Z]*)*$", message = "Surname is invalid.")  
    private String surname;  
  
    @Email(message = "Enter a valid email address.")  
    private String email;  
```

Using an external message source for error messages is better but I didn’t use it because it would make this article longer.

Then create a RestController and accept User as a `@Valid` input.

```java
@RestController  
@RequestMapping("/api/users")  
public class UserRestController {  
  
    @PostMapping  
    public ResponseEntity<User> saveUser(@Valid @RequestBody User user) {  
  
        return ResponseEntity.ok(user);  
    }  
}
```

> Do not forget to use `@Valid` annotation. Because it makes sure that the annotated parameter is validated.

Run the application and send an example request. (You don’t have to use cURL of course. I sent this request with Postman. You can just copy this and import it as a raw text to Postman.)

```java
curl --location --request POST 'http://localhost:8080/api/users' \
--header 'Content-Type: application/json' \
--data-raw '{
	"name": "Marion",
	"surname": "Cotillard",
	"email": "marion@cotillard.com",
	"birthdate": "1975-09-30"
}'
```

This request will return 200 as could be expected. Now let’s break some rules:

```curl
curl --location --request POST 'http://localhost:8080/api/users' \
--header 'Content-Type: application/json' \
--data-raw '{
	"name": "M",
	"surname": "Cotillard",
	"email": "marion@cotillard.com",
	"birthdate": "1975-09-30"
}'
```

I just broke `@Length` rule on `name` field. This request responded with a long response:

```json
{
  "timestamp": "2020-02-01T21:27:06.935+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "Length.user.name",
        "Length.name",
        "Length.java.lang.String",
        "Length"
      ],
      "arguments": [
        {
          "codes": [
            "user.name",
            "name"
          ],
          "arguments": null,
          "defaultMessage": "name",
          "code": "name"
        },
        30,
        2
      ],
      "defaultMessage": "Name must be between 2-30 characters. ",
      "objectName": "user",
      "field": "name",
      "rejectedValue": "M",
      "bindingFailure": false,
      "code": "Length"
    }
  ],
  "message": "Validation failed for object='user'. Error count: 1",
  "path": "/api/users"
}
```

This can be a confusing response but it’s possible to create a custom response.

### Override Exception Handler

Firstly, create a POJO for the custom response.

```java
@Getter  
@Setter  
@Builder  
@AllArgsConstructor  
public class CustomFieldError {  
  
	private String field;  
  
	private String message;  
  
}
```

You can add extra fields but this is enough for this article. Then create an exception handler method:

```java
@ExceptionHandler(MethodArgumentNotValidException.class)  
public final ResponseEntity<Object> handleUserMethodFieldErrors(MethodArgumentNotValidException ex, WebRequest request) {  
	// some logic
}
```

Javax Annotations throws `MethodArgumentNotValidException.class` so, overrode this exception. In the method, extract field errors and create `CustomFieldError` objects from them.

```java
final List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();    

final List<CustomFieldError> customFieldErrors = new ArrayList<>();  
 
for (FieldError fieldError : fieldErrors) {  
 
	final String field = fieldError.getField();  
 
	final String message = fieldError.getDefaultMessage();  
 
	final CustomFieldError customFieldError = CustomFieldError.builder().field(field).message(message).build();  
 
	customFieldErrors.add(customFieldError);  
 
}
```

You can also get `rejectedValue` and `errorCode`. Then create an HTTP response and return.

```java
return ResponseEntity.badRequest().body(customFieldErrors);
```

Run the project again and send the same request. This will return 400 response with the body below:

```json
[
	{
		"field": "name",
		"message": "Name must be between 2-30 characters. "
	}
]
```

You can customise this response or override similar exception handlers with the same approach.

Github Repo: [https://github.com/kamer/custom-javax-annotation-error-handling](https://github.com/kamer/custom-javax-annotation-error-handling)

* * *

For questions, suggestions or corrections feel free to reach me on: 

**Email:** [kamer@kamerelciyar.com](mailto:kamer@kamerelciyar.com) 
**Twitter:** [https://twitter.com/kamer\_ee](https://twitter.com/kamer_ee)