developer
developer

Reputation: 9478

how to avoid displaying rejected value if spring validation is failed at request bean validation

From below log we can see rejected value is display user data(example: User PII data with some special characters)

[Field error in object 'Customer' on field 'FirstName': rejected value [robert% steve];

So we tried to use @ControllerAdvice, MethodArgumentNotValidException and customize default error msg to show defined error msg.

But somehow this approach is not working for us with feature testcases. So do there any configuration not to display rejected value? or to show rejected value with masking?

thanks.

Upvotes: 1

Views: 2004

Answers (4)

muhammed ozbilici
muhammed ozbilici

Reputation: 772

You can create an @InitBinder for that, then validate your field before hand over :

@InitBinder
public void initBinder(WebDataBinder binder) {
   binder.addValidators(new FirstNameValidate());
}

public class FirstNameValidate implements Validator {

   @Override 
   public boolean supports(Class<?> arg0) {
       // code implementation ...
   }

   @Override 
   public void validate(Object target, Errors errors) {
       // code implementation ...
   }
}

Upvotes: 0

aatwork
aatwork

Reputation: 2270

I believe I have found the right solution here. I was not sure that "rejected value" throws MethodArgumentNotValidException. Once I verified that, other things fall into the right place.

Cause:

Spring already has a base class ResponseEntityExceptionHandler which handles MethodArgumentNotValidException specifically using handleMethodArgumentNotValid() method. So your method in @ControllerAdvice class is never called.

Solution:

Override ResponseEntityExceptionHandler.handleMethodArgumentNotValid() method & add your own custom logic there.

Sample code:

@RestControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String, String> error = Map.of("message", "Field value not valid.");
        return handleExceptionInternal(ex, error, headers, HttpStatus.BAD_REQUEST, request);
    }
}

Upvotes: 3

unconditional
unconditional

Reputation: 7666

If you'd like to see validation exceptions logged, but without the field values, you can add your own HandlerExceptionResolver that'll log your MethodArgumentNotValidException the way you need. Here's an example:

@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        int beforeDefaultExceptionResolver = resolvers.size() - 1;
        resolvers.add(beforeDefaultExceptionResolver, new DefaultHandlerExceptionResolver() {

            @Override
            protected String buildLogMessage(Exception ex, HttpServletRequest request) {
                if (ex instanceof MethodArgumentNotValidException validationEx) {
                    // example, modified from org.springframework.web.bind.MethodArgumentNotValidException#getMessage
                    StringBuilder sb = new StringBuilder("Validation failed for argument [")
                            .append(validationEx.getParameter().getParameterIndex()).append("] in ")
                            .append(validationEx.getParameter().getExecutable().toGenericString());
                    BindingResult bindingResult = validationEx.getBindingResult();
                    if (bindingResult.getErrorCount() > 1) {
                        sb.append(" with ").append(bindingResult.getErrorCount()).append(" errors");
                    }
                    sb.append(": ");
                    for (ObjectError error : bindingResult.getAllErrors()) {
                        sb.append('[');
                        if (error instanceof FieldError fieldError) {
                            sb.append("Field error in object '" + fieldError.getObjectName() + "' on field '" + fieldError.getField() + "'");
                        }
                        else {
                            sb.append(error);
                        }
                        sb.append("] ");
                    }
                    return sb.toString();
                }

                return super.buildLogMessage(ex, request);
            }
        });
    }
}

If you don't want to see validation exceptions logged at all, just set the following loggers' level to ERROR in your application.yml:

logging:
  level:
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver: error
    org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver: error

Upvotes: 1

Mor Blau
Mor Blau

Reputation: 420

You can set your own custom validation annotation, set your own validation rules and rejection message:

@Documented
@Constraint(validatedBy = MyValidator.class) 
@Target( { ElementType.METHOD, ElementType.FIELD }) // set the desired context
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValidation {
    String message() default "Validation error! Not going to display rejected value.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

You can also set multiple validators in the @Constraint, and they will run consecutively.

The validator:

public class MyValidator implements 
  ConstraintValidator<MyValidation, String> { // here you set the validated field type, e.g. String

    @Override
    public void initialize(MyValidation value) {
    }

    @Override
    public boolean isValid(String value,
        ConstraintValidatorContext cxt) {
        ... // some validation logic
        return true/false;
    }

}

Then simply add your annotation to the validated field. You can even change the message at that point:

@MyValidation(message = "Error!")
String validField;

For more deep-dive info, you can check https://www.baeldung.com/spring-mvc-custom-validator

Upvotes: 2

Related Questions