我有一个@AroundInvoke REST Web服务拦截器,我想用它来记录常见数据,如类和方法,远程IP地址和响应时间.
使用InvocationContext获取类和方法名称很简单,只要被拦截的Rest服务在其参数列表中包含@Context HttpServletRequest,就可以通过HttpServletRequest获得远程IP.
但是有些REST方法的参数中没有HttpServletRequest,在这些情况下我无法弄清楚如何获取HttpServletRequest对象.
例如,以下REST Web服务没有@Context HttpServletRequest参数
@Inject @Default private MemberManager memberManager; @POST @Path("/add") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Member add(NewMember member) throws MemberInvalidException { return memberManager.add(member); }
我已经尝试将它直接注入我的拦截器,但是(在JBoss 6.1上)它总是为空…
public class RestLoggedInterceptorImpl implements Serializable { @Context HttpServletRequest req; @AroundInvoke public Object aroundInvoke(InvocationContext ic) throws Exception { logger.info(req.getRemoteAddr()); // <- this throws NPE as req is always null ... return ic.proceed();
我想建议一种可靠的方法来访问HttpServletRequest对象 – 甚至只是Http Headers …无论REST服务是否包含参数.
解决方法
在研究Javadoc
http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html中的拦截器生命周期之后,我认为不可能访问除InvocationContext之外的任何servlet上下文信息(由底层REST定义中的参数定义).这是因为Interceptor实例具有相同的生命周期作为底层bean,并且必须将Servlet Request @Context注入方法而不是实例.但是,如果方法签名中除了InvocationContext之外还有其他内容,则不会部署包含@AroundInvoke的Interceptor;它不接受额外的@Context参数.
因此,允许Interceptor获取HttpServletRequest的唯一答案是修改底层REST方法定义以包含@Context HttpServletRequest参数(如果需要,还包括HttpServletResponse).
@Inject @Default private MemberManager memberManager; @POST @Path("/add") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public Member add(NewMember member,@Context HttpServletRequest request,@Context HttpServletResponse response) throws MemberInvalidException { ... }
然后,拦截器可以遍历InvocationContext中的参数以获取HttpServletRequest
@AroundInvoke public Object aroundInvoke(InvocationContext ic) throws Exception { HttpServletRequest req = getHttpServletRequest(ic); ... return ic.proceed(); } private HttpServletRequest getHttpServletRequest(InvocationContext ic) { for (Object parameter : ic.getParameters()) { if (parameter instanceof HttpServletRequest) { return (HttpServletRequest) parameter; } } // ... handle no HttpRequest object.. e.g. log an error,throw an Exception or whatever