3
votes

I am using spring boot 2.0.6, hibernate validator 6.0, validation-api 2.0, and apache cxf. I used @Valid annotation in controler method and then i set @NotNull, @Email with proper messages in bean properties. Then I created a CustomExceptionHandler to handle the MethodArgumentNotValidException. while testing it by postman i am not getting the proper response with my validation message.

UserController.class

@Path("/user")
@Component
public class UserController {

@POST
@Path("/register")
@Consumes(MediaType.APPLICATION_JSON_VALUE)
@Produces(MediaType.APPLICATION_JSON_VALUE)
public Response registerUser(@Valid User user) {

    String status = "";

    Boolean isEmailIdExist = UserAuthService.checkUserByMailId(user.getEmail());
    Boolean isUserNameExist = UserAuthService.checkUserByUserName(user.getUserName());

    if(!isEmailIdExist) {
        if(!isUserNameExist) {
            status = UserAuthService.registerUser(user);
        }else {
            status = "Username already taken. Try with different name";
        }
    }else {
        status = "Email ID already exist";
    }

    return Response.ok(status).build();
}
}

User.class

@Entity
@Table(name = "USER")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "UID")
private int userId;

@NotBlank(message = "Please provide first name")
@Column(name = "FIRST_NAME", length = 50)
private String firstName;

@NotBlank(message = "Please provide email")
@Column(name = "EMAIL", length = 100, unique = true)
@Email(message = "Please provide a valid email")
private String email;}

getter/setter
}

CustomExceptionHandler.java

@ControllerAdvice
@Component
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
    return new ResponseEntity<Object>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
        HttpHeaders headers, HttpStatus status, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(new Date(), "Validation Failed", ex.getBindingResult().toString());
    return new ResponseEntity<Object>(errorDetails, HttpStatus.BAD_REQUEST);
}
}

Json Structure

{
    "firstName":"",
    "email":"[email protected]",
}
3
Can you provide what you are expecting and what you are getting? - Pooja Aggarwal
Hi @PoojaAggarwal. U can see my json structure. I am sending this json in postman and i should get a response with validation message "Please provide first name" but i am getting full exception stacktrace. - Prakash kumar mallick
Can you comment this method handleMethodArgumentNotValid and try. - Pooja Aggarwal
Again i got same exception. In eclipse console i am able to see the validation message. - Prakash kumar mallick
List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='Please provide first name', propertyPath=firstName, rootBeanClass=class com.airbus.argo.model.ArgoUser, messageTemplate='Please provide first name'} ]] - Prakash kumar mallick

3 Answers

4
votes

If your experiencing the issue of for example: not being able to see the validation errors (default-messages) returned back to the client, this is what you could do:

Top Solution 1: Simply add devtools. This should solve the issue. After I did this, all my binding-results were returned back to the client. I recommend you to test this out first:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
</dependency>

Solution 2:

I found out that this is due to using Spring Boot 2.3+ So if youre using Spring Boot 2.3 or higher, add this dependency in your pom.xml file as its no longer included within the 'web'-dependency itself.

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Now its necessary to set 'include binding errors' in java/resources/application.properties to "always". Same goes for 'message' as well although I think this is optional.

server.error.include-message=always
server.error.include-binding-errors=always

Solution 3: (before I discovered solution 2 which could be helpful as well)

So I found out that this is due to having Spring boot 2.3+. But I could not find caution-messages on the new updated usage of @Valid in Spring Boot v2.3+.

So I ended up switching back to Spring boot v2.2.10 (latest version of 2.2) by adjusting the release version in the pom.xml file like so:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.10.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

This worked perfectly for me by rolling back to an older version. Although id like to update my Spring Boot version some day. (Revisit solution 1 & 2)

0
votes

A custom class-level constraint validator throws MethodArgumentNotValidException when validation fails in my application. I have extracted the error message from bindingResult of this exception as shown below:

@RestControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex, HttpHeaders headers,
            HttpStatus status, WebRequest request) {
        //to extract the default error message from a diagnostic
        // information about the errors held in MethodArgumentNotValidException
        Exception exception = new Exception(ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
        return this.createResponseEntity(HttpStatus.BAD_REQUEST, exception, request);
    }

private ResponseEntity<Object> createResponseEntity(
            HttpStatus httpStatus, Exception ex, WebRequest request) {
        ErrorResponse errorResponse = ErrorResponse.builder()
                .timestamp(LocalDateTime.now())
                .status(httpStatus.value())
                .error(httpStatus.getReasonPhrase())
                .message(ex.getMessage())
                .path(request.getDescription(true))
                .build();
        return handleExceptionInternal(ex, errorResponse,
                new HttpHeaders(), httpStatus, request);
    }

}

ErrorResponse class:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {

    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String message;
    private String path;
}

I hope this helps. If you need a detailed explanation on class-level constraint, have a look at this video.

0
votes

From Springboot 2.3+ below dependency need to be added for validation

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Adding above dependency will do the validation and logg the message in the cosole. However if you want to get the message with http response then below need to be added to application.properties file in resource folder

server.error.include-message=always
server.error.include-binding-errors=always