Java 如何在 Jersey 调用之前获取与 URI 匹配的资源方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16303694/
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
How to get resource method matched to URI before Jersey invokes it?
提问by Paul Bellora
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.
我正在尝试实现一个ContainerRequestFilter
对请求参数进行自定义验证的方法。我需要查找将与 URI 匹配的资源方法,以便我可以从方法的参数中抓取自定义注释。
Based on this answerI should be able to inject ExtendedUriInfo
and then use it to match the method:
基于这个答案,我应该能够注入ExtendedUriInfo
然后使用它来匹配方法:
public final class MyRequestFilter implements ContainerRequestFilter {
@Context private ExtendedUriInfo uriInfo;
@Override
public ContainerRequest filter(ContainerRequest containerRequest) {
System.out.println(uriInfo.getMatchedMethod());
return containerRequest;
}
}
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).
但getMatchedMethod
显然返回null
,一直到该方法被实际调用(此时我进行验证为时已晚)。
How can I retrieve the Method
that will be matched to a given URI, before the resource method is invoked?
Method
在调用资源方法之前,如何检索将与给定 URI 匹配的 URI?
For those interested, I'm trying to roll my own required parameter validation, as described in JERSEY-351.
对于那些感兴趣的人,我正在尝试推出自己所需的参数验证,如JERSEY-351 中所述。
采纳答案by Paul Bellora
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.
我想出了如何仅使用泽西岛来解决我的问题。显然无法将请求的 URI 与在调用该方法之前匹配的方法进行匹配,至少在 Jersey 1.x 中是这样。但是,我能够使用 a为每个单独的资源方法ResourceFilterFactory
创建一个ResourceFilter
- 这样这些过滤器可以提前知道目标方法。
Here's my solution, including the validation for required query params (uses Guava and JSR 305):
这是我的解决方案,包括对所需查询参数的验证(使用 Guava 和 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;
}
}
}
And the ResourceFilterFactory
is registered with Jersey as an init param of the servlet in web.xml
:
并且在ResourceFilterFactory
Jersey 中注册为 servlet 的初始化参数web.xml
:
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>my.package.name.ValidationFilterFactory</param-value>
</init-param>
At startup, ValidationFilterFactory.create
gets called for each resource method detected by Jersey.
在启动时,ValidationFilterFactory.create
为 Jersey 检测到的每个资源方法调用。
Credit goes to this post for getting me on the right track: How can I get resource annotations in a Jersey ContainerResponseFilter
归功于这篇文章让我走上了正确的轨道:How can I get resource annotations in a Jersey ContainerResponseFilter
回答by condit
I know you're looking for a Jersey only solution but here's a Guice approach that should get things working:
我知道您正在寻找仅限 Jersey 的解决方案,但这里有一种 Guice 方法可以让事情正常进行:
public class Config extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(
new JerseyServletModule() {
@Override
protected void configureServlets() {
bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor());
bind(Service.class);
Map<String, String> params = Maps.newHashMap();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.example");
serve("/*").with(GuiceContainer.class, params);
}
});
}
public static class ValidationInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation method) throws Throwable {
System.out.println("Validating: " + method.getMethod());
return method.proceed();
}
}
}
@Path("/")
public class Service {
@GET
@Path("service")
@Produces({MediaType.TEXT_PLAIN})
public String service(@QueryParam("name") String name) {
return "Service " + name;
}
}
EDIT: A performance comparison:
编辑:性能比较:
public class AopPerformanceTest {
@Test
public void testAopPerformance() {
Service service = Guice.createInjector(
new AbstractModule() {
@Override
protected void configure() { bindInterceptor(Matchers.inSubpackage("org.example"), Matchers.any(), new ValidationInterceptor()); }
}).getInstance(Service.class);
System.out.println("Total time with AOP: " + timeService(service) + "ns");
}
@Test
public void testNonAopPerformance() {
System.out.println("Total time without AOP: " + timeService(new Service()) + "ns");
}
public long timeService(Service service) {
long sum = 0L;
long iterations = 1000000L;
for (int i = 0; i < iterations; i++) {
long start = System.nanoTime();
service.service(null);
sum += (System.nanoTime() - start);
}
return sum / iterations;
}
}
回答by dthorpe
In resteasy-jaxrs-3.0.5, you can retrieve a ResourceMethodInvoker
representing the matched resource method from ContainerRequestContext.getProperty()
inside a ContainerRequestFilter
:
在 resteasy-jaxrs-3.0.5 中,您可以ResourceMethodInvoker
从ContainerRequestContext.getProperty()
a 内部检索表示匹配资源方法的 a ContainerRequestFilter
:
import org.jboss.resteasy.core.ResourceMethodInvoker;
public class MyRequestFilter implements ContainerRequestFilter
{
public void filter(ContainerRequestContext request) throws IOException
{
String propName = "org.jboss.resteasy.core.ResourceMethodInvoker";
ResourceMethodInvoker invoker = (ResourceMethodInvoker)request.getProperty();
invoker.getMethod().getParameterTypes()....
}
}
回答by Christian Gürtler
Actually, you should try to inject ResourceInfo
into your custom request filter. I have tried it with RESTEasy and it works there. The advantage is that you code against the JSR interfaces and not the Jersey implementation.
实际上,您应该尝试注入ResourceInfo
您的自定义请求过滤器。我已经用 RESTEasy 试过了,它在那里工作。优点是您针对 JSR 接口而不是 Jersey 实现进行编码。
public class MyFilter implements ContainerRequestFilter
{
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext requestContext)
throws IOException
{
Method theMethod = resourceInfo.getResourceMethod();
return;
}
}