25

对于在 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.xx 可行吗?如果不是,这种验证的最佳方法是什么?

谢谢。

4

6 回答 6

41

Spring 不支持处理程序方法@javax.validation.Valid中的@PathVariable注释参数。有一个改进请求,但仍未解决

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

于 2013-10-17T15:21:16.600 回答
23

您可以像这样使用: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 初始化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 {
...
}

step.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));
        }
    }
}
于 2016-10-26T08:14:45.357 回答
5

解决方案很简单:

@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 需要被验证,就像任何用户输入一样。

于 2018-10-07T05:47:06.217 回答
3

您可以利用 Spring MVC 将路径变量映射到 bean 的能力,而不是使用 @PathVariable:

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

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

}

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

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

确保您的路径变量 ( {id}) 对应于 bean 字段 ( id);

于 2019-07-19T18:50:53.930 回答
2

@PathVariable并不意味着为了向用户发送回可读消息而进行验证。作为原则,pathVariable 永远不应该是无效的。如果 pathVariable 无效,原因可能是:

  1. 一个错误生成了一个错误的 url(例如 jsp 中的一个 href)。不需要@Valid也不需要消息,只需修复代码即可;
  2. “用户”正在操作 url。同样,@Valid不需要,不应该向用户提供有意义的消息。

在这两种情况下,只要让一个异常冒泡,直到它被通常的 Spring ExceptionHandlers 捕获,以便生成一个漂亮的错误页面或一个有意义的 json 响应来指示错误。为了获得此结果,您可以使用自定义编辑器进行一些验证。

创建一个CustomerNumber类,可能是不可变的(实现 aCharSequence不是必需的,但允许您基本上像使用它一样使用它 a 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) {
    ...
}
于 2015-01-17T17:43:22.350 回答
0

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

于 2013-10-17T07:37:57.193 回答