从泽西岛过滤器或拦截器内部访问资源方法参数.或者将 AOP 与资源方法一起使用



我正在尝试使用用户的ID丰富每个请求的SLF4J MDC。问题在于 ID 可以通过多种方式传递,有时作为路径参数传递,有时在正文中传递,有时由首先解密它的自定义ValueFactoryProvider注入。

如果我能以某种方式访问所有注入的(即已经反序列化的(参数值,我可以轻松处理所有这些情况。

例如

对于资源,例如:

@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 ...;
}

我可以有一个过滤器,例如:

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();
}
}

但是我看不到从ContainerRequestFilter拦截器或其他任何东西实现somehowGetTheValue的方法......

Jersey 在引擎盖下使用 HK2 进行依赖注入。HK2 支持 AOP。您的用例的一个选项是使用此 AOP 支持。您需要做的就是实现一个MethodInterceptor和一个InterceptionService。在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;
}
}

要使用侦听器,请配置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;
}
}

确定应在getMethodInterceptors()方法中截获哪个方法。如果该方法应该被截获,则返回拦截器列表,否则返回 null。处理此问题的常用方法是创建自定义注释并仅注释该方法。在上面的方法中,只需检查

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

要使这一切正常工作,您只需要在HK2注册InteceptionService即可。您可以在AbstractBinder中执行此操作,这是泽西岛应用程序中用于配置 DI 的功能。

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

您可以在此 GitHub 存储库中看到一个完整的示例。HK2网站上还有一个官方例子。只需查看帖子顶部的链接"AOP支持"。

你可以这样得到它

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));

基本上,这个想法是复制流并进行任何处理,然后设置流。因为如果你不这样做,那么在你的控制器方法中你会得到 null,因为流已经被读取了。

最新更新