Search code examples
javaspringvalidationweb-servicespostman

propertyPath being appended in error messages


This is my validation class which implements ConstraintValidator

@Slf4j
public class DocumentMetaDataValidation implements ConstraintValidator<DocumentMetaDataValidator, DocumentMetaDataApplicationRequest> {

    @Override
    public boolean isValid(DocumentMetaDataApplicationRequest dataApplicationRequest, ConstraintValidatorContext constraintValidatorContext) {

        constraintValidatorContext.disableDefaultConstraintViolation();
        if(!Objects.nonNull(dataApplicationRequest)) {
            log.error("Request Parameter is null/empty : ");
            constraintValidatorContext
                    .buildConstraintViolationWithTemplate("Required parameter DocumentMetaDataApplicationRequest cannot be null/empty")
                    .addConstraintViolation();
            return false;
        }
        Set<String> documentTypeSet=new HashSet<>();
        dataApplicationRequest.getDocumentDetails().stream().forEach(documentDetails ->
            documentTypeSet.add(documentDetails.getDocumentType()));

        if (documentTypeSet.size()!=dataApplicationRequest.getDocumentDetails().size()){
            log.error("Duplicate document type present ");
            constraintValidatorContext
                    .buildConstraintViolationWithTemplate("Duplicate document type present")
                    .addConstraintViolation();
            return false;
        }

        return true;
    }
}

This is my controller :

@PostMapping(path = "/{referenceId}")
  public Mono<DocumentUploadResponse> upload(
      @NotBlank(message = "reference Id cannot be blank")
      @PathVariable(value = "referenceId") String referenceId,
      @NotEmpty(message = "List should not be empty")
      @DocumentValidator @RequestPart(value = "documents") List<FilePart> document,
      @DocumentMetaDataValidator @Valid @NotNull(message = "DocumentMetadata cannot be null")
      @RequestPart(value = "documentsMetaData") DocumentMetaDataApplicationRequest documentMetaDataApplicationRequest) {

    log.info("inside DocumentStorageController : uploadDocument()");
    return documentStorageService.upload(referenceId, document, documentMetaDataApplicationRequest);

  }

This is my validator class

@Target( { FIELD, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = DocumentMetaDataValidation.class)
public @interface DocumentMetaDataValidator {
    public String message() default ErrorMessage.VALIDATION_ERROR;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Instead of getting error as "Duplicate document type present" im getting "upload.DocumentMetaDataApplicationRequest : Duplicate document type present" where upload is the method in my controller and DocumentMetaDataApplicationRequest is the class that I am trying to validate.

I tried to go through documentation It seems its is appending something called "propertyPath" which Im not able to get rid of.


Solution

  • You can override the behaviour by implementing custom exception handler

    @RestControllerAdvice
    public class GlobalExceptionHandler extends DefaultHandlerExceptionResolver {
    
        @ResponseStatus(BAD_REQUEST)
        @ExceptionHandler(value = {MethodArgumentNotValidException.class})
        protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception) {
            Map<String, String> errors = new HashMap<>();
            exception.getBindingResult().getAllErrors().forEach((error) -> {
                String fieldName = ((FieldError) error).getField();
                String errorMessage = error.getDefaultMessage();
                errors.put(fieldName, errorMessage);
            });
            return status(BAD_REQUEST).body(Collections.singletonMap("errors", errors));
        }
    }
    

    In case of ConstraintVoilationException you can add new exception handler

    @ResponseStatus(BAD_REQUEST)
    @ExceptionHandler(value = {ConstraintViolationException.class})
    protected ResponseEntity<Object> handleConstraintVoilation(ConstraintViolationException exception) {
        var violations = exception.getConstraintViolations();
        var errs = violations.
            stream().
            map(ConstraintViolation::getMessage).
            toList();
        Map<String, String> errors = new HashMap<>();
        return status(BAD_REQUEST).body(Collections.singletonMap("errors", errs));
    }