【问题标题】:Spring Data REST: custom methods validationSpring Data REST:自定义方法验证
【发布时间】:2017-03-06 07:04:48
【问题描述】:

我正在尝试将带有@RepositoryRestResource 注释的Spring Data REST 存储库与自定义方法实现一起使用。 有两种情况:

1) 我的 REST 存储库使用 @RepositoryRestResource 注释,它映射到 /users 端点。另外,我有@RestController,它映射到同一个端点。 这会导致 @RepositoryRestResource 中的方法(应该公开)不可见,并在它们上得到 405 结果。 但是使用 @Valid 注释的方法验证正在处理 @RestController 方法。例如这行得通:

@ResponseBody
@RequestMapping(value = "/users")
public ResponseEntity signUp(@RequestBody @Valid final UserSignUpRequest userSignUpRequest)

2) 与 REST 存储库一起工作的控制器是 @RepositoryRestController 控制器。这样@RepositoryRestController@RepositoryRestResource 中声明的两种方法都可以正常工作。但是方法上的JSR-303 @Valid 注释停止工作,所以我不能使用@Valid 注释。这个问题已经在DATAREST-593进行了描述。

任何想法如何解决两个问题中的至少一个?主要思想是使用@RepositoryRestResource 存储库以及自定义控制器方法和注释验证。

【问题讨论】:

    标签: java spring validation spring-mvc spring-data-rest


    【解决方案1】:

    您也可以将其添加到您的 @RepositoryRestController :

    @Inject
    private LocalValidatorFactoryBean validator;
    
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(validator);
    }
    

    【讨论】:

    • 您的解决方案看起来非常紧凑。它有效,我收到 400 错误,但响应的 json 与“标准”SDR 存储库不同,有没有办法自定义它?
    【解决方案2】:

    这种情况下好像没有好的解决办法,而且@Valid注解默认无论如何都不支持,见DATAREST-593。这就是为什么,为了支持 @RepositoryRestController 方法上的 @Valid 注释,我创建了以下 @ControllerAdvice 类:

    package com.tivoli.api.application.advice;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.validation.BeanPropertyBindingResult;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.validation.Validator;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
    
    import javax.validation.Valid;
    import javax.validation.ValidationException;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Type;
    
    /**
     * Workaround class for making JSR-303 annotation validation work for controller method parameters.
     * Check the issue <a href="https://jira.spring.io/browse/DATAREST-593">DATAREST-593</a>
     */
    @ControllerAdvice
    public class RequestBodyValidationProcessor extends RequestBodyAdviceAdapter {
    
        private final Validator validator;
    
        public RequestBodyValidationProcessor(@Autowired final Validator validator) {
            this.validator = validator;
        }
    
        @Override
        public boolean supports(final MethodParameter methodParameter, final Type targetType, final Class<? extends
                HttpMessageConverter<?>> converterType) {
            final Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
            for (final Annotation annotation : parameterAnnotations) {
                if (annotation.annotationType().equals(Valid.class)) {
                    return true;
                }
            }
    
            return false;
        }
    
        @Override
        public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage, final MethodParameter
                parameter, final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) {
            final Object obj = super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
            final BindingResult bindingResult = new BeanPropertyBindingResult(obj, obj.getClass().getCanonicalName());
            validator.validate(obj, bindingResult);
            if (bindingResult.hasErrors()) {
                throw new ValidationException(createErrorMessage(bindingResult));
            }
    
            return obj;
        }
    
        private String createErrorMessage(final BindingResult bindingResult) {
            final StringBuilder stringBuilder = new StringBuilder("Invalid parameters specified.");
            if (bindingResult.getFieldErrors() != null && !bindingResult.getFieldErrors().isEmpty()) {
                stringBuilder.append(" Fields:");
                bindingResult.getFieldErrors().forEach(fieldError -> stringBuilder
                        .append(" [ ")
                        .append(fieldError.getField())
                        .append(" : ")
                        .append(fieldError.getRejectedValue())
                        .append(" ] "));
            } else if (bindingResult.getAllErrors() != null && !bindingResult.getAllErrors().isEmpty()) {
                final ObjectError objectError = bindingResult.getAllErrors().get(0); // get the first error
                stringBuilder.append(" Message: ")
                        .append(objectError.getDefaultMessage());
            }
    
            return stringBuilder.toString();
        }
    }
    

    【讨论】:

    • 感谢分享。您的解决方案产生的响应与从标准 SDR 存储库返回的响应非常不同。此外,在您的情况下,返回 500 http 状态而不是 4xx。有什么建议吗?
    • 您可以在 createErrorMessage 方法中自定义响应。对于不同的状态码,只需在对应的异常处理程序中捕获ValidationException即可。
    猜你喜欢
    • 2016-02-14
    • 2016-03-02
    • 2014-08-10
    • 2016-03-26
    • 1970-01-01
    • 2018-04-18
    • 2016-05-21
    • 2014-02-02
    相关资源
    最近更新 更多