Java REST 端点 Spring Boot 中参数的自定义验证逻辑
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38661177/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom validation logic for parameter in REST endpoint Spring Boot
提问by ptimson
I currently have this RequestMapping
where I use validation through a regular expression:
我目前有这个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...
}
However, the regulare expression is not enough. Instead, I would like to do some custom validation on the header attribute i.e.
然而,正则表达式是不够的。相反,我想对 header 属性进行一些自定义验证,即
if (!API_KEY_LIST.contains(apiKeyHeader)) {
throw Exception();
}
Is this possible?
这可能吗?
回答by Evgeni Dimitrov
1) Check manually
1)手动检查
You can inject the HttpServletRequest and check the headers.
您可以注入 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) Inject the header
2)注入头部
@RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity<String> test(@RequestHeader(value="myheader") String myheader){
return new ResponseEntity<String>(HttpStatus.OK);
}
That will return:
那将返回:
{
"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"
}
if the header is missing.
如果标题丢失。
3) Use filter
3) 使用过滤器
You can automate the check with some filter if you want to use it on multiple methods. In your custom filter just get the header(like in method 1) and if the header is missing just respond with 400 or whatever you want. For me that makes sense when you don't use the header value in the controller method and only need to validate that it's present.
如果您想在多种方法上使用它,您可以使用一些过滤器自动检查。在您的自定义过滤器中,只需获取标题(如方法 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();
}
Skip the request
跳过请求
If you use headers attribute in @RequestMapping
如果在@RequestMapping 中使用 headers 属性
@RequestMapping(value = "/test", method = RequestMethod.POST,
headers = {"content-type=application/json"})
this will result in 404 if there ain't no other handler to take the request.
如果没有其他处理程序接受请求,这将导致 404。
回答by Ulises
Best way to do this IMO is to create a custom HandlerMethodArgumentResolver
that would look something like this using a custom annotation @Segment
:
执行此 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;
}
}
}
Then your controller signature looks like this:
然后您的控制器签名如下所示:
@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...
}
You'll register the handler method argument resolver in you WebMvcConfigurationAdapter:
您将在 WebMvcConfigurationAdapter 中注册处理程序方法参数解析器:
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(segmentHandler());
}
@Bean
public SegmentHandlerMethodArgumentResolver segmentHandler() {
return new SegmentHandlerMethodArgumentResolver();
}
}
回答by Sarvesh Dubey
There is aleardy a feature request Spring backlog, checkout JIRA. However, i was able to achieve what you are trying using @Validated
annotation on Controller.
有一个功能请求 Spring backlog,结帐JIRA。但是,我能够@Validated
在 Controller 上使用注释来实现您正在尝试的功能。
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
@GetMapping("/{loginId}")
public User getUserBy(@PathVariable @LoginID final String loginId) {
// return some user
}
}
Here @LoginID
is custom validator. And @Validated
is from org.springframework.validation.annotation.Validated
which does the trick.
这@LoginID
是自定义验证器。并且@Validated
是从org.springframework.validation.annotation.Validated
哪个技巧。
回答by Pruthviraj
Just add the following class. Do any validations inside "doFilter" method and set appropriate response code.
只需添加以下类。在“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);
}
}