简体   繁体   English

从Jersey过滤器或拦截器内部访问资源方法参数。 或者使用AOP和资源方法

[英]Access resource method arguments from inside a Jersey filter or interceptor. Or use AOP with resource method

I'm trying to enrich the SLF4J MDC on each request with the user's ID. 我正在尝试使用用户的ID在每个请求上丰富SLF4J MDC。 The problem is that the ID can be passed in many ways, sometimes as a path parameter, sometimes in the body, and sometimes injected by a custom ValueFactoryProvider that first decrypts it. 问题是ID可以以多种方式传递,有时作为路径参数传递,有时在体内,有时由首先解密它的自定义ValueFactoryProvider注入。

If I could somehow access all the injected (ie already deserialized ) parameter values, I could handle all these cases easily. 如果我可以以某种方式访问​​所有注入(即已经反序列化 )的参数值,我可以轻松处理所有这些情况。

Eg 例如

For a resource such as: 对于以下资源:

@GET
//@Encrypted params are injected by a custom ValueFactoryProvider
public Something getSomething(@Encrypted("userId") String userId) {
    return ...;
}

@POST
public Something getSomething(@RequestBody RequestWithUserId requestWithUserId) {
    return ...;
}

I could have a filter such as: 我可以有一个过滤器,如:

public class MdcFilter implements ContainerRequestFilter, ContainerResponseFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        Method theMethod = resourceInfo.getResourceMethod();
        for (Parameter parameter : theMethod.getParameters()) {
            //Deal with the @Encrypted case
            if (parameter.isAnnotationPresent(Encrypted.class) && parameter.getAnnotation(Encrypted.class).value().equals("userId")) {
                MDC.put("userId", somehowGetTheValue());
            }
            //Deal with the @RequestBody case
            if (parameter.isAnnotationPresent(RequestBody.class) && parameter.getType().equals(RequestWithUserId.class)) {
                MDC.put("userId", ((RequestWithUserId)somehowGetTheValue()).getUserId());
            }
            ... //other possibilities
        }
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        MDC.clear();
    }
}

But I don't see a way to implement somehowGetTheValue either from a ContainerRequestFilter an interceptor or anything else... 但我没有看到从ContainerRequestFilter拦截器或其他任何东西实现somehowGetTheValue的方法......

Jersey uses HK2 under the hood for dependency injection. 泽西使用引擎盖下的HK2进行依赖注入。 And HK2 has AOP support . HK2有AOP支持 One option for your use case would be use this AOP support. 您的用例的一个选项是使用此AOP支持。 All you need to do is implement a MethodInterceptor and an InterceptionService . 您需要做的就是实现一个MethodInterceptor和一个InterceptionService In the MethodInterceptor , you can get all the arguments from the MethodInvocation and you can get parameter annotation from the Method MethodInterceptor ,您可以从MethodInvocation获取所有参数,并且可以从Method获取参数注释

class MyMethodInteceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Object[] args = invocation.getArguments();

        // do your logging or whatever with the args.

        // invoke method and get return value.
        Object returnValue = invocation.proceed();
        // if you want to do something with the return
        // value before returning it, you can.

        return returnValue;
    }
}

To use the interceptor, you configure the InterceptionService . 要使用拦截器,请配置InterceptionService

public class MyInterceptionService implements InterceptionService {

    private final static MethodInterceptor METHOD_INTERCEPTOR 
            = new MyMethodInterceptor();
    private final static List<MethodInterceptor> METHOD_LIST
            = Collections.singletonList(METHOD_INTERCEPTOR);

    @Override
    public Filter getDescriptorFilter() {
        return BuilderHelper.allFilter();
    }

    @Override
    public List<MethodInterceptor> getMethodInterceptors(Method method) {
        // you implement shouldIntercept
        if (shouldIntercept(method)) {
            return METHOD_LIST;
        }
        return null;
    }

    @Override
    public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
        return null;
    }
}

You determine which method should be intercepted in the getMethodInterceptors() method. 您可以确定在getMethodInterceptors()方法中应截取哪个方法。 If the method should be intercepted, then return a list of interceptors, otherwise return null. 如果该方法应该被截获,则返回一个拦截器列表,否则返回null。 A common way of handling this is to create a custom annotation and just annotate the method. 处理此问题的常用方法是创建自定义注释并仅注释该方法。 The in the above method, just check 在上面的方法中,只需检查

if (method.isAnnotationPresent(YourAnno.class)) {
    return METHOD_LIST;
}

To make it all work, you just need to register the InteceptionService with HK2. 要使一切正常,您只需要使用HK2注册InteceptionService You can do that in an AbstractBinder , which is what is used in a Jersey app to configure your DI. 您可以在AbstractBinder执行此操作,这是Jersey应用程序中用于配置DI的内容。

ResourceConfig config = new ResourceConfig();
config.register(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(MyInterceptionService.class)
                .to(InterceptionService.class)
                .in(Singleton.class);
    }
});

You can see a complete example in this GitHub repo . 你可以在这个GitHub仓库中看到一个完整的例子。 There is also an official example in the HK2 site. 在HK2网站上也有一个官方的例子。 Just see "AOP support" the link at the top of the post. 只需在帖子顶部看到“AOP支持”链接即可。

You can get it like this 你可以这样得到它

StringWriter stringWriter = new StringWriter();
IOUtils.copy(new InputStreamReader(requestContext.getEntityStream()), stringWriter);
System.out.println(stringWriter.toString());// String representation of the payload
requestContext.setEntityInputStream(new ByteArrayInputStream(requestEntity));

Basically the idea is to copy the stream and do any processing and then set the stream back. 基本上,我们的想法是复制流并进行任何处理,然后重新设置流。 Because if you don't do that, then in your controller method you would get null, becuase the stream was already read. 因为如果你不这样做,那么在你的控制器方法中你会得到null,因为已经读取了流。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM