【问题标题】:Custom validation logic for parameter in REST endpoint Spring BootREST端点Spring Boot中参数的自定义验证逻辑
【发布时间】:2016-12-04 07:26:51
【问题描述】:

我目前有这个RequestMapping,我通过正则表达式使用验证:

 @RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @RequestHeader(value = "X-API-Key", required = true) @Valid @Pattern(regexp = SEGMENTS_REGEX) String apiKeyHeader) {

     // Stuff here...
 }

但是,正则表达式是不够的。相反,我想对标头属性进行一些自定义验证,即

if (!API_KEY_LIST.contains(apiKeyHeader)) {
    throw Exception();
}

这可能吗?

【问题讨论】:

  • @RequestHeader(value = "X-API-Key", required = true) @Valid @Pattern(regexp = SEGMENTS_REGEX) String apiKeyHeader 你能告诉我上面的行适合你吗?
  • 已有功能请求 Spring backlog,结帐jira.spring.io/browse/SPR-6380。但是,我能够在 Controller 上使用 @Validated 注释来实现您正在尝试的功能。
  • 请注意,虽然this issue 未解析,但@PathVariable@RequestHeader@RequestParam 上的验证注释仅在使用@Validated 注释类时才有效。以防有人想知道为什么@Pattern 为 OP 工作......
  • 你能分享一些关于@Validated & validate headers 的例子吗?

标签: java spring validation spring-boot spring-rest


【解决方案1】:

1) 手动检查

您可以注入 HttpServletRequest 并检查标头。

@RestController
public class HomeController {
    public ResponseEntity<String> test(HttpServletRequest request){
        if(request.getHeader("apiKeyHeader") == null){
            return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
        }
        return new ResponseEntity<String>(HttpStatus.OK);
    }
}

2) 注入标头

@RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity<String> test(@RequestHeader(value="myheader") String myheader){
    return new ResponseEntity<String>(HttpStatus.OK);
}

这将返回:

{
  "timestamp": 1469805110889,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.web.bind.ServletRequestBindingException",
  "message": "Missing request header 'myheader' for method parameter of type String",
  "path": "/test"
}

如果缺少标题。

3) 使用过滤器

如果您想在多种方法上使用它,您可以使用一些过滤器自动检查。在您的自定义过滤器中,只需获取标题(如方法 1 中所示),如果标题丢失,只需使用 400 或您想要的任何内容进行响应。对我来说,当您不在控制器方法中使用标头值并且只需要验证它是否存在时,这是有意义的。

@Bean
public FilterRegistrationBean someFilterRegistration() { 
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(apiHeaderFilter());
    registration.addUrlPatterns("/example/*");
    registration.setName("apiHeaderFilter");
    registration.setOrder(1);
    return registration;
} 

@Bean(name = "ApiHeaderFilter")
public Filter apiHeaderFilter() {
    return new ApiHeaderFilter();
}

跳过请求

如果你在 @RequestMapping 中使用 headers 属性

@RequestMapping(value = "/test", method = RequestMethod.POST,
    headers = {"content-type=application/json"})

如果没有其他处理程序接受请求,这将导致 404。

【讨论】:

  • 对于选项#3:如果标头未正确发送,我们将如何使用此过滤器代码返回错误的请求响应?
  • @IcedD​​ante 在这里查看答案:stackoverflow.com/questions/23621037/…
【解决方案2】:

执行此 IMO 的最佳方法是创建一个自定义 HandlerMethodArgumentResolver,使用自定义注释 @Segment 看起来像这样:

public class SegmentHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(String.class)
            && parameter.getParameterAnnotation(Segment.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String apiKey = webRequest.getHeader("X-API-Key");
        if (apiKey != null) {
            if (!API_KEY_LIST.contains(apiKey)) {
                throw new InvalidApiKeyException();
            }
            return apiKey;
        } else {
            return WebArgumentResolver.UNRESOLVED;
        }
    }
}

那么你的控制器签名是这样的:

@RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @Segment String apiKeyHeader) {

     // Stuff here...
 }

您将在 WebMvcConfigurationAdapter 中注册处理程序方法参数解析器:

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(segmentHandler());
    }

    @Bean
    public SegmentHandlerMethodArgumentResolver segmentHandler() {
        return new SegmentHandlerMethodArgumentResolver();
    }

}

【讨论】:

  • 为什么将其命名为“Segment”?
  • parameter.getParameterAnnotation(Segment.class);应该是 parameter.getParameterAnnotation(Segment.class) != null
【解决方案3】:

已有功能请求 Spring 积压,结帐JIRA。但是,我能够在 Controller 上使用 @Validated 注释来实现您正在尝试的功能。

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

   @GetMapping("/{loginId}")
   public User getUserBy(@PathVariable @LoginID final String loginId) {
      // return some user
   }
}

这里的@LoginID 是自定义验证器。 @Validated 来自 org.springframework.validation.annotation.Validated,这可以解决问题。

【讨论】:

【解决方案4】:

只需添加以下类。 在“doFilter”方法中进行任何验证并设置适当的响应代码。

@Configuration
public class ApiHeaderFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String token = request.getHeader("token");
        if (StringUtil.isNullOrEmpty(token)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
            return;
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

【讨论】:

    猜你喜欢
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 2017-06-28
    • 2020-04-12
    • 2017-03-17
    • 2017-01-21
    • 2021-09-25
    • 2021-09-05
    相关资源
    最近更新 更多