16

I need to have my own error response body when something goes wrong with my request and I am trying to use the @NotEmpty constraint message attribute to return the error message,

This is my class that returns the error message using the body that I need:

package c.m.nanicolina.exceptions;


import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class CustomResponseEntityExceptionHandler {

    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public ResponseEntity<ApiError> handleConflict(MissingServletRequestParameterException ex, WebRequest request) {
        ApiError apiError = new ApiError(ex.getMessage(), ex.getMessage(), 1000);
        return new ResponseEntity<ApiError>(apiError, null, HttpStatus.BAD_REQUEST);
    }
}

With this CustomResponseEntityExceptionHandler I can return my own response body in case of validation errors.

What I am trying now is to get the message from the validation constraints.

This is my controller with the NotEmpty constraint:

package c.m.nanicolina.controllers;

import c.m.nanicolina.models.Product;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.constraints.NotEmpty;

@RestController
public class MinimumStockController {

    @RequestMapping(value = "/minimumstock")
    public Product product(
            @RequestParam(value = "product.sku") @NotEmpty(message = "Product.sku cannot be empty") String sku,
            @RequestParam(value = "stock.branch.id") String branchID) {
        return null;
    }
}

In my exception, I can't find a way to get that message Product.sku cannot be empty and show it in my error response.

I have also checked the class MissingServletRequestParameterException and there is the method getMessage which is returning the default message.

0

5 Answers 5

23

Yes it is doable & spring very well supports it. You are just missing some configuration to enable it in spring.

  • Use Spring@Validated annotation to enable spring to validate controller
  • Handle ConstraintViolationException in your ControllerAdvice to catch all failed validation messages.
  • Mark required=false in @RequestParam, so it will not throw MissingServletRequestParameterException and rather move to next step of constraint validation.
@ControllerAdvice
public class CustomResponseEntityExceptionHandler {

    @ExceptionHandler
  public ResponseEntity<ApiError> handle(ConstraintViolationException exception) {
        //you will get all javax failed validation, can be more than one
        //so you can return the set of error messages or just the first message
        String errorMessage = new ArrayList<>(exception.getConstraintViolations()).get(0).getMessage();
       ApiError apiError = new ApiError(errorMessage, errorMessage, 1000);    
       return new ResponseEntity<ApiError>(apiError, null, HttpStatus.BAD_REQUEST);
  }
}



@RestController
@Validated
public class MinimumStockController {

    @RequestMapping(value = "/minimumstock")
    public Product product(
            @RequestParam(value = "product.sku", required=false) @NotEmpty(message = "Product.sku cannot be empty") String sku,
            @RequestParam(value = "stock.branch.id", required=false) String branchID) {
        return null;
    }
}

NOTE: MissingServletRequestParameterException won't have access to javax validation messages, as it is thrown before constraint validation occurs in the request lifecycle.

Sign up to request clarification or add additional context in comments.

Comments

1

Yes it is possible. Do this:

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ArgumentsErrorResponseDTO> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {

    ServiceException exception = ServiceException.wrap(ex, ErrorCode.FIELD_VALIDATION);
    BindingResult results = ex.getBindingResult();
    for (FieldError e: results.getFieldErrors()) {
        exception.addLog(e.getDefaultMessage(), e.getField());
    }
    // log details in log
    log.error("Invalid arguments exception: {}", exception.logWithDetails(), exception);
    return ResponseEntity.status(exception.getErrorCode().getHttpStatus())
            .body(ArgumentsErrorResponseDTO.builder()
                    .code(exception.getErrorCode().getCode())
                    .message(exception.getMessage())
                    .details(exception.getProperties())
                    .build());
}

Comments

1

If this can help, I found the solution for this issue here: https://howtodoinjava.com/spring-boot2/spring-rest-request-validation/

You have to add this method to your CustomResponseEntityExceptionHandler class:

 @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<String> details = new ArrayList<>();
        for(ObjectError error : ex.getBindingResult().getAllErrors()) {
            details.add(error.getDefaultMessage());
        }
        ErrorMessage error = new ErrorMessage(new Date(), details.toString());
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }

Comments

0

You should put this on your handler.

@ControllerAdvice
public class CustomResponseEntityExceptionHandler {

    @ExceptionHandler(value = { MissingServletRequestParameterException.class })
    public ResponseEntity<ApiError> handleConflict(MissingServletRequestParameterException ex, WebRequest request) {
        String message = ex.getParameterName() + " cannot be empty";
        ApiError apiError = new ApiError(ex.getMessage(), message, 1000);
        return new ResponseEntity < ApiError > (apiError, null, HttpStatus.BAD_REQUEST);
    }
}

UPDATE

I don't know how you can get a default message, but as a workaround, you could do the validation on your controller and throw an custom exception if the parameter is empty, then handle in your CustomResponseEntityExceptionHandler.

Something like the following:

Set required=false

@RequestMapping(value = "/minimumstock")
public Product product(@RequestParam(required = false) String sku, @RequestParam(value = "stock.branch.id") String branchID) {
    if (StringUtils.isEmpty(sku)) 
        throw YourException("Product.sku cannot be empty");

    return null;
}

2 Comments

But is there a way for me to get the message from the @NotEmpty annotation in my controller?
@Gerep I don't know if you can do it, but I've updated my answer with a workaround. Hope this helps.
-1

enter image description here

enter image description here

Use @ExceptionHandler(MethodArgumentNotValidException.class)

@ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> validationHandler(MethodArgumentNotValidException exception) {
        HashMap<String, Object> resObj = new HashMap<String, Object>();
        String errorMsg = "validation is failed!";
        if (exception.getErrorCount() > 0) {
            List <String> errorDetails = new ArrayList<>();
            for (ObjectError error : exception.getBindingResult().getAllErrors()) {
                errorDetails.add(error.getDefaultMessage());
            }

            if (errorDetails.size() > 0) errorMsg = errorDetails.get(0);
        }
        
        resObj.put("status", GlobalConst.BAD_REQUEST_CODE);
        resObj.put("message", errorMsg);
        return new ResponseEntity<>(resObj, HttpStatus.OK);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.