如何在 Jersey 调用它之前获取与 URI 匹配的资源方法?
我正在尝试实现 ContainerRequestFilter
对请求的参数进行自定义验证.我需要查找将与 URI 匹配的资源方法,以便我可以从方法的参数中抓取自定义注释.
I'm trying to implement a ContainerRequestFilter
that does custom validation of a request's parameters. I need to look up the resource method that will be matched to the URI so that I can scrape custom annotations from the method's parameters.
基于 this answer 我应该能够注入 ExtendedUriInfo
然后用它来匹配方法:
Based on this answer I should be able to inject ExtendedUriInfo
and then use it to match the method:
public final class MyRequestFilter implements ContainerRequestFilter {
@Context private ExtendedUriInfo uriInfo;
@Override
public ContainerRequest filter(ContainerRequest containerRequest) {
System.out.println(uriInfo.getMatchedMethod());
return containerRequest;
}
}
但是 getMatchedMethod
显然返回 null
,一直到该方法被实际调用(此时我进行验证为时已晚).
But getMatchedMethod
apparently returns null
, all the way up until the method is actually invoked (at which point it's too late for me to do validation).
在调用资源方法之前,如何检索将与给定 URI 匹配的 Method
?
How can I retrieve the Method
that will be matched to a given URI, before the resource method is invoked?
对于那些感兴趣的人,我正在尝试推出自己的必需参数验证,如 JERSEY 中所述-351.
For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351.
推荐答案
我想出了如何仅使用 Jersey 来解决我的问题.至少在 Jersey 1.x 中,显然没有办法将请求的 URI 与在调用该方法之前将匹配的方法相匹配.但是,我能够使用 ResourceFilterFactory
为每个单独的资源方法创建一个 ResourceFilter
- 这样这些过滤器可以提前知道目标方法.
I figured out how to solve my problem using only Jersey. There's apparently no way to match a request's URI to the method that will be matched before that method is invoked, at least in Jersey 1.x. However, I was able to use a ResourceFilterFactory
to create a ResourceFilter
for each individual resource method - that way these filters can know about the destination method ahead of time.
这是我的解决方案,包括对所需查询参数的验证(使用 Guava 和 JSR 305):
Here's my solution, including the validation for required query params (uses Guava and JSR 305):
public final class ValidationFilterFactory implements ResourceFilterFactory {
@Override
public List<ResourceFilter> create(AbstractMethod abstractMethod) {
//keep track of required query param names
final ImmutableSet.Builder<String> requiredQueryParamsBuilder =
ImmutableSet.builder();
//get the list of params from the resource method
final ImmutableList<Parameter> params =
Invokable.from(abstractMethod.getMethod()).getParameters();
for (Parameter param : params) {
//if the param isn't marked as @Nullable,
if (!param.isAnnotationPresent(Nullable.class)) {
//try getting the @QueryParam value
@Nullable final QueryParam queryParam =
param.getAnnotation(QueryParam.class);
//if it's present, add its value to the set
if (queryParam != null) {
requiredQueryParamsBuilder.add(queryParam.value());
}
}
}
//return the new validation filter for this resource method
return Collections.<ResourceFilter>singletonList(
new ValidationFilter(requiredQueryParamsBuilder.build())
);
}
private static final class ValidationFilter implements ResourceFilter {
final ImmutableSet<String> requiredQueryParams;
private ValidationFilter(ImmutableSet<String> requiredQueryParams) {
this.requiredQueryParams = requiredQueryParams;
}
@Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
@Override
public ContainerRequest filter(ContainerRequest request) {
final Collection<String> missingRequiredParams =
Sets.difference(
requiredQueryParams,
request.getQueryParameters().keySet()
);
if (!missingRequiredParams.isEmpty()) {
final String message =
"Required query params missing: " +
Joiner.on(", ").join(missingRequiredParams);
final Response response = Response
.status(Status.BAD_REQUEST)
.entity(message)
.build();
throw new WebApplicationException(response);
}
return request;
}
};
}
@Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
}
}
并且 ResourceFilterFactory
在 Jersey 中注册为 web.xml
中 servlet 的 init 参数:
And the ResourceFilterFactory
is registered with Jersey as an init param of the servlet in web.xml
:
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.name.ValidationFilterFactory</param-value>
</init-param>
在启动时,Jersey 检测到的每个资源方法都会调用 ValidationFilterFactory.create
.
At startup, ValidationFilterFactory.create
gets called for each resource method detected by Jersey.
感谢这篇文章让我走上了正轨:如何在 Jersey ContainerResponseFilter 中获取资源注释
Credit goes to this post for getting me on the right track: How can I get resource annotations in a Jersey ContainerResponseFilter
相关文章