'AssertTrue validation does not give right error message in jhi-alert-error

I have added a validation in my DTO like this:

@AssertTrue(message = "validation.amountNot0RateAbove1" )
public boolean isAmountNot0RateAbove1() {
  return amount == 0 ? true : rate != null && rate > 1;
}

this gives an error like this:

Resolved exception caused by Handler execution: org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument at index 0 in method: public org.springframework.http.ResponseEntity<nl.tibi.sbys.service.dto.ProjectDTO> nl.tibi.sbys.web.rest.client.ClientProjectResource.updateProject(nl.tibi.sbys.service.dto.ProjectDTO) throws java.net.URISyntaxException, with 1 error(s): [Field error in object 'projectDTO' on field 'position[0].amountNot0RateAbove1': rejected value [false]; codes [AssertTrue.projectDTO.position[0].amountNot0RateAbove1,AssertTrue.projectDTO.position.amountNot0RateAbove1,AssertTrue.position[0].amountNot0RateAbove1,AssertTrue.position.amountNot0RateAbove1,AssertTrue.amountNot0RateAbove1,AssertTrue.boolean,AssertTrue]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [projectDTO.position[0].amountNot0RateAbove1,position[0].amountNot0RateAbove1]; arguments []; default message [position[0].amountNot0RateAbove1]]; default message [validation.amountNot0RateAbove1]] 

the response is (type httpErrorResponse):

error:
fieldErrors:
Array(1)0:
    field:"position[0].amountNot0RateAbove1"
    message:"AssertTrue"
    objectName:"projectDTO"

jhi-alert-error shows this message:

translation-not-found[error.AssertTrue]

what i would like to show is a custom error message so with amountNot0RateAbove1 key.

somehow the errors from the java code are not send to the front-end only the last key AssertTrue is send.

how should a change it?

i think it would be best if the first error from the java code would be send as message and not the last. so AssertTrue.projectDTO.position[0].amountNot0RateAbove1 instead or even better take the default message if set which is the message from the annotation:

@AssertTrue(message = "validation.amountNot0RateAbove1" )

any help?

as a not so nice workaround i tried to edit the respons setting the message in the line of this:

private onSaveError(res: HttpErrorResponse) {
    console.log('res:', res);
    res.error.fieldErrors[0].message = 'validation.amountNot0RateAbove1';
    console.log('res:', res);
    this.isSaving = false;
}

but although the code is executed it did not change the message.

this, src/main/webapp/app/shared/alert/alert-error.component.ts component makes the error message. i could do something there unless someone has a better solution :)

i think ExceptionTranslator is the place to be for improving the returned error message.

===========================edit==========================

here it can be changed. not sure what the effects will be for other errors:

@Override
public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
    BindingResult result = ex.getBindingResult();
    List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
        .map(f -> new FieldErrorVM(f.getObjectName(), f.getField(), f.getCode()))
        .collect(Collectors.toList());

the f.getCode() will return the last code in the list which is the most general one. the first is the most specific. seconde the message set in the AssertTrue annotation is found in the f.getDefaultMessage()

not sure if i should go for the first code or the default message



Solution 1:[1]

I still hope someone has a better solution but this is what i did. This code will keep the default way of working which is getting the most general message from the fielderror with getCode. But in rare cases you can overwrite it in this way:

ExceptionTranslator

  public static final String USE_MESSAGE_AS_KEY = "USE_MESSAGE_AS_KEY:";

  @Override
  public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
    BindingResult result = ex.getBindingResult();
    List<FieldErrorVM> fieldErrors = new ArrayList<>();
    for (FieldError fieldError : result.getFieldErrors()) {
      String errorCode = null;
      if (StringUtils.startsWith(fieldError.getDefaultMessage(), USE_MESSAGE_AS_KEY)) {
        errorCode = StringUtils.removeStart(fieldError.getDefaultMessage(), USE_MESSAGE_AS_KEY);
      }else {
        errorCode = fieldError.getCode();
      }
      fieldErrors.add(new FieldErrorVM(fieldError.getObjectName(), fieldError.getField(), errorCode));
    }

    Problem problem = Problem.builder()
      .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
      .withTitle("Method argument not valid")
      .withStatus(defaultConstraintViolationStatus())
      .with("message", ErrorConstants.ERR_VALIDATION)
      .with("fieldErrors", fieldErrors)
      .build();
    return create(ex, problem, request);
  }

In a DTO you can now add this:

  @AssertTrue(message = USE_MESSAGE_AS_KEY + "amountNot0RateAbove1")
  public boolean isRate() {
    return amount == 0 ? true : rate != null && rate > 0;
  }

and it will use the message amountNot0RateAbove1 (prefixed with error.) as key to translate in the frontent to a nice error message.

Solution 2:[2]

I had similar issue - but I just wanted to output the @AssertTrue message in the response. I used this:

@Override
public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
    BindingResult result = ex.getBindingResult();
    List<FieldErrorVM> fieldErrors = result.getFieldErrors().stream()
            .map(this::createFieldErrorVM)
            .collect(Collectors.toList());

    Problem problem = Problem.builder()
            .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
            .withTitle("Method argument not valid")
            .withStatus(defaultConstraintViolationStatus())
            .with("message", ErrorConstants.ERR_VALIDATION)
            .with("fieldErrors", fieldErrors)
            .build();
    return create(ex, problem, request);
}

private FieldErrorVM createFieldErrorVM(FieldError fieldError) {
    String errorCode;
    if (fieldError.getCode().equals("AssertTrue")) {
        errorCode = fieldError.getDefaultMessage();
    }else {
        errorCode = fieldError.getCode();
    }
    return new FieldErrorVM(fieldError.getObjectName(), fieldError.getField(), errorCode);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 tibi
Solution 2 Cursive