如何验证Spring MVC @PathVariable值?

时间:2013-10-17 05:55:48

标签: spring validation spring-mvc

对于在Spring MVC中实现的简单RESTful JSON api,我可以使用Bean Validation(JSR-303)来验证传递给处理程序方法的路径变量吗?

例如:

 @RequestMapping(value = "/number/{customerNumber}")
 @ResponseBody
 public ResponseObject searchByNumber(@PathVariable("customerNumber") String customerNumber) {
 ...
 }

在这里,我需要使用Bean验证来验证customerNumber变量的长度。这可能是Spring MVC v3.x.x吗?如果没有,这种验证的最佳方法是什么?

感谢。

6 个答案:

答案 0 :(得分:26)

Spring在处理程序方法中对@javax.validation.Valid带注释的参数不支持@PathVariable。有一个改进请求,但它仍然是unresolved

最好的办法是在处理程序方法体中进行自定义验证,或者考虑使用其他答案中建议的org.springframework.validation.annotation.Validated

答案 1 :(得分:13)

您可以这样使用: 使用org.springframework.validation.annotation.Validated来有效RequestParamPathVariable

 *
 * Variant of JSR-303's {@link javax.validation.Valid}, supporting the
 * specification of validation groups. Designed for convenient use with
 * Spring's JSR-303 support but not JSR-303 specific.
 *

step.1 init ValidationConfig

@Configuration
public class ValidationConfig {
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
        return processor;
    }
}

step.2将@Validated添加到控制器处理程序类,如:

@RequestMapping(value = "poo/foo")
@Validated
public class FooController {
...
}

步骤3将validators添加到您的处理程序方法:

   @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
   public ResponseEntity<Foo> delete(
           @PathVariable("id") @Size(min = 1) @CustomerValidator int id) throws RestException {
        // do something
        return new ResponseEntity(HttpStatus.OK);
    }

最后一步。将异常解析器添加到您的上下文中:

@Component
public class BindExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (ex.getClass().equals(BindException.class)) {
            BindException exception = (BindException) ex;

            List<FieldError> fieldErrors = exception.getFieldErrors();
            return new ModelAndView(new MappingJackson2JsonView(), buildErrorModel(request, response, fieldErrors));
        }
    }
}

答案 2 :(得分:2)

@PathVariable无意验证,以便向用户发回可读消息。作为原则,pathVariable永远不应该是无效的。如果pathVariable无效,原因可能是:

  1. 一个错误生成了一个坏网址(例如jsp中的一个href)。没有@Valid 需要,不需要任何消息,只需修复代码;
  2. &#34;用户&#34;正在操纵网址。 同样,不需要@Valid,用户也不应该有意义的消息 给予。
  3. 在这两种情况下,只需留下异常气泡,直到它被捕获为止     通常的Spring ExceptionHandlers为了生成一个不错的     错误页面或指示错误的有意义的json响应。在     为了获得此结果,您可以使用自定义编辑器进行一些验证。

    创建一个CustomerNumber类,可能是不可变的(不需要实现CharSequence但允许你使用它,就好像它是String

    public class CustomerNumber implements CharSequence {
    
        private String customerNumber;
    
        public CustomerNumber(String customerNumber) {
            this.customerNumber = customerNumber;
        }
    
        @Override
        public String toString() {
            return customerNumber == null ? null : customerNumber.toString();
        }
    
        @Override
        public int length() {
            return customerNumber.length();
        }
    
        @Override
        public char charAt(int index) {
            return customerNumber.charAt(index);
        }
    
        @Override
        public CharSequence subSequence(int start, int end) {
            return customerNumber.subSequence(start, end);
        }
    
        @Override
        public boolean equals(Object obj) {
            return customerNumber.equals(obj);
        }
    
        @Override
        public int hashCode() {
            return customerNumber.hashCode();
        }
    }
    

    创建一个实现验证逻辑的编辑器(在这种情况下没有空格和固定长度,仅作为示例)

    public class CustomerNumberEditor extends PropertyEditorSupport {
    
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
    
            if (StringUtils.hasText(text) && !StringUtils.containsWhitespace(text) && text.length() == YOUR_LENGTH) {
                setValue(new CustomerNumber(text));
            } else {
                throw new IllegalArgumentException();
                // you could also subclass and throw IllegalArgumentException
                // in order to manage a more detailed error message
            }
        }
    
        @Override
        public String getAsText() {
            return ((CustomerNumber) this.getValue()).toString();
        }
    }
    

    在Controller中注册编辑器

    @InitBinder
    public void initBinder(WebDataBinder binder) {
    
        binder.registerCustomEditor(CustomerNumber.class, new CustomerNumberEditor());
        // ... other editors
    }
    

    更改接受CustomerNumber而不是String的控制器方法的签名(无论您的ResponseObject是什么......)

    @RequestMapping(value = "/number/{customerNumber}")
    @ResponseBody
    public ResponseObject searchByNumber(@PathVariable("customerNumber") CustomerNumber customerNumber) {
        ...
    }
    

答案 3 :(得分:1)

解决方案很简单:

@GetMapping(value = {"/", "/{hash:[a-fA-F0-9]{40}}"})
public String request(@PathVariable(value = "hash", required = false) String historyHash)
{
    // Accepted requests: either "/" or "/{40 character long hash}"
}

是的,与任何用户输入一样,PathVariables也需要验证。

答案 4 :(得分:0)

路径变量可能未与系统中的任何bean链接。你想用JSR-303注释注释什么? 要验证路径变量,您应该使用此方法Problem validating @PathVariable url on spring 3 mvc

答案 5 :(得分:0)

代替使用@PathVariable,您可以利用Spring MVC的功能将路径变量映射到Bean中:

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/{id}")
    public void get(@Valid GetDto data) {
        // data.getId() is the path variable
    }

}

并且Bean包含实际的验证规则:

@Data
public class GetDto {
     @Min(1) @Max(99)
     private long id;
}

确保路径变量({id})与bean字段(id)相对应;