如何在失败时打印验证错误

时间:2016-08-26 12:35:27

标签: java spring-mvc spring-boot bean-validation

给出以下dto和控制器

public class PasswordCredentials implements AuthenticationProvider {

    @NotNull
    @NotEmpty
    @JsonProperty( access = JsonProperty.Access.WRITE_ONLY )
    private String user;

    @NotNull
    @NotEmpty
    @JsonProperty( access = JsonProperty.Access.WRITE_ONLY )
    private CharSequence pass;


    public void setPass( final CharSequence pass ) {
        this.pass = pass;
    }

    public void setUser( final String user ) {
        this.user = user;
    }

    @Override
    public Authentication toAuthentication() {
        return new UsernamePasswordAuthenticationToken( user, pass );
    }
}

@RestController
@RequestMapping( path = "authentication" )
class AuthenticationController {
    private final AuthenticationManager am;

    AuthenticationController( final AuthenticationManager am ) {
        this.am = am;
    }

    @RequestMapping( path = "password", method = RequestMethod.POST, consumes = {
        "!" + MediaType.APPLICATION_FORM_URLENCODED_VALUE
    } )
    ResponseEntity<?> login( @Valid @RequestBody final PasswordCredentials credentials ) {
        Authentication authenticate = am.authenticate( credentials.toAuthentication() );
        if ( authenticate.isAuthenticated() ) {
            return ResponseEntity.status( HttpStatus.NO_CONTENT ).build();
        }
        return ResponseEntity.status( HttpStatus.FORBIDDEN ).build();
    }

}

如果例如pass为空,则会出现验证错误,并且在没有调用我的控制器的情况下会发生400,这很好。然而,400有没有内容,有没有办法让控制器BindResults作为内容输出,以便API的消费者知道导致问题的原因?理想情况下我不会这样做控制器方法,以便它会发生在所有控制器上?

我能够通过如下弹簧数据休息获得此行为,但我喜欢所有API控制器。

class RestConfig extends RepositoryRestConfigurerAdapter {

    @Bean
    Validator validator() {
        return new LocalValidatorFactoryBean();
    }


    @Override
    public void configureValidatingRepositoryEventListener(
            final ValidatingRepositoryEventListener validatingListener ) {
        Validator validator = validator();
        //bean validation always before save and create
        validatingListener.addValidator( "beforeCreate", validator );
        validatingListener.addValidator( "beforeSave", validator );
    }

    @Override
    public void configureRepositoryRestConfiguration( final RepositoryRestConfiguration config ) {
        config.setBasePath( "/v0" );
        config.setReturnBodyOnCreate( false );
        config.setReturnBodyOnUpdate( false );
    }

3 个答案:

答案 0 :(得分:0)

Spring有@ControllerAdvice@ExceptionHandler注释来处理控制器中的错误。

@ControllerAdvice
public class ExceptionTranslator {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public Error processValidationError(MethodArgumentNotValidException ex) {
         BindingResult result = ex.getBindingResult();
        .....
        return new Error();
    }

    // Other exceptions
}

答案 1 :(得分:0)

我想改进Anton Novopashin的答案:只返回响应实体中的错误。

@ControllerAdvice
public class ExceptionTranslator {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseEntity<String> processValidationError(MethodArgumentNotValidException ex) {
        return new ResponseEntity(ex.getMessage, HttpStatus.BAD_REQUEST);
    }

    // Other exceptions
}

答案 2 :(得分:0)

我不确定是谁或为什么对现有答案进行了低估,但它们都是正确的 - 处理验证错误的最佳方法是声明@ControllerAdvice然后处理那里的例外。这是我的全局错误处理程序的片段,取自现有项目:

@ControllerAdvice
@ResponseBody
public class RestfulErrorHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ErrorResponse methodValidationError(MethodArgumentNotValidException e) {
        final ErrorResponse response = new ErrorResponse();
        for (ObjectError error : e.getBindingResult().getAllErrors()) {
            if (error instanceof FieldError) {
                response.addFieldError((FieldError) error);
            } else {
                response.addGeneralError(error.getDefaultMessage());
            }
        }
        return response;
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public ErrorResponse constraintViolationError(ConstraintViolationException e) {
        final ErrorResponse response = new ErrorResponse();
        for (ConstraintViolation<?> v : e.getConstraintViolations()) {
            response.addFieldError(v.getPropertyPath(), v.getMessage());
        }

        return response;
    }
}

您也应该处理ConstraintViolationException,因为它们也可能被抛出。这是a link to my ErrorResponse class,我将其作为一个要点包括在内,以免模糊主要观点。

您还应该处理RepositoryConstraintViolationException,我不确定spring-data-rest是否已经处理过它们。