Spring 5 PathVariable验证导致Http 404

时间:2019-06-15 19:36:26

标签: java spring validation spring-mvc

当我将@Validated添加到Spring RestConstroller时,我收到了该Controllers端点的HTTP 404响应。在添加注释之前,发现端点没有任何问题。

问题特别是与@PathVariable有关,并且我可以使用非基本RequestBody参数正确验证(使用@Valid)。

关于此的文章很多,我花了大量时间尝试几种变体而没有成功。最简单的是。

根据这个简单的示例https://sdqali.in/blog/2015/12/05/validating-requestparams-and-pathvariables-in-spring-mvc/,我认为它应该与向控制器添加@Validated,为PathVariable参数保留@Valid并添加MethodValidationPostProcessor一样简单豆角,扁豆。最好我用@Valid代替@Validated

我正在使用Spring 5(不是启动),并且在类路径上具有hibernate-validator 6.0.16.Final

问题性RestController

@Validated
@RestController
@RequestMapping(value = "practices")
public class RestPracticeResource implements PracticeResource {

    @Override
    @GetMapping("/{id}")
    public ResponseEntity<Practice> getPractice(@Id @PathVariable("id") final String id) throws ResourceNotFoundException {
        final UUID uuidId = UUID.fromString(id);
    }
}

为RequestBody使用RestController

@RestController
@RequestMapping(value = "accounts")
public class RestAccountResource implements AccountResource {

    @Override
    @PostMapping
    public ResponseEntity<AccountDto> create(@NotNull @Valid @RequestBody final CreateAccountDto createAccountDto)
            throws ResourceAlreadyExistsException {
        ...
    }
}

ID注释

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = Id.IdValidator.class)
public @interface Id {

    // ... Stock standard code here

    class IdValidator implements ConstraintValidator<Id, String> {

        @Override
        public void initialize(final Id id) {}

        @Override
        public boolean isValid(final String id, final ConstraintValidatorContext constraintValidatorContext) {
            // Basic REGEX match for UUID format
            return ValidationUtil.isValidId(id);
        }
    }
}

ExceptionHander

@ExceptionHandler(ConstraintViolationException.class)
public final ResponseEntity<ApiError> handleConstraintViolationException(final ConstraintViolationException ex,
                                                                         final WebRequest webRequest) {
    // .. extract violations into standard format
    return new ResponseEntity<>(apiError, BAD_REQUEST);
}

MethodValidationPostProcessor bean

@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor();
}

实际结果

一旦添加@Validated,我将收到/practices/{id}端点的Http 404响应,无论我提供哪种格式的ID。如果删除@Validated,则可以传递有效的UUID,并且一切正常,或者传递无效的UUID,并从UUID.fromString(id)引发异常。

如果我根据请求正文验证尝试使用@Id,则会忽略

@Valid

预期结果

添加@Validated将启用@Id批注验证,并且在提供非UUID格式的ConstraintViolationException时抛出{id}

3 个答案:

答案 0 :(得分:0)

这是一个与此类似的老问题,我找到了我想要的解决方案。链接:Spring rest controller @RequestParam validation

总结Szymon Stepniak的答案。这是spring-mvc错误。我使用的是休眠版本5.2.4.Final和spring-webmvc 4.3.8.RELEASE版本。

如果rest控制器实现了一个接口,并且我们还添加了@Validated注释,则RequestMappingHandlerMapping将不会注册端点。我不知道根本原因。但是删除“实现接口”对我来说解决了这个问题。

答案 1 :(得分:0)

使用代理目标类,它将与 @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

答案 2 :(得分:0)

我添加了您所做的常用内容(pom 中的库、RequestBody 上的 @Valid 等),并且也得到了 404!

<dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.3.Final</version>
        </dependency>
        <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b11</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

Spring 文档(和许多博客)的微妙之处在于 Spring 会寻找一个退出点来抛出错误,但如果该退出点不存在,它将回复 404。在阅读了很多之后,尤其是这个 { {3}},添加这个类让 Spring 识别 @Valid 并找到一个退出点来抛出错误

    @RestControllerAdvice
    @Order(1) 
    public class RestControllerExceptionHandler {

    @RequestMapping(value = { "error/404" }, method = RequestMethod.GET)
    @ExceptionHandler(Exception.class)
    public String handleUnexpectedException(Exception e) {
        return "views/base/rest-error";         
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String handlemethodArgumentNotValid(MethodArgumentNotValidException exception) { //
        // TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
        return "Sorry, that was not quite right: " + exception.getMessage();
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public String handleConstraintViolation(ConstraintViolationException exception) { //
        // TODO you can choose to return your custom object here, which will then get transformed to json/xml etc.
        return "Sorry, that was not quite right: " + exception.getMessage();
    }

    
}