sho*_*ser 6 jboss jax-rs interceptor java-ee-6 jboss-weld
我有一个@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);
}
Run Code Online (Sandbox Code Playgroud)
我已经尝试将它直接注入我的拦截器,但是(在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();
Run Code Online (Sandbox Code Playgroud)
我想建议一种可靠的方法来访问HttpServletRequest对象 - 甚至只是Http Headers ...无论REST服务是否包含参数.
在研究了Javadoc中的拦截器生命周期之后http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html我不认为可以访问除此之外的任何servlet上下文信息. InvocationContext(由底层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 {
...
}
Run Code Online (Sandbox Code Playgroud)
然后,拦截器可以遍历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
Run Code Online (Sandbox Code Playgroud)
避免在每个REST方法中创建其他参数的另一种解决方法是为所有使用这种拦截器的REST服务创建一个超类:
public abstract class RestService {
@Context
private HttpServletRequest httpRequest;
// Add here any other @Context fields & associated getters
public HttpServletRequest getHttpRequest() {
return httpRequest;
}
}
Run Code Online (Sandbox Code Playgroud)
因此,原始REST服务可以在不更改任何方法签名的情况下对其进行扩展:
public class AddService extends RestService{
@POST
@Path("/add")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member) throws MemberInvalidException {
return memberManager.add(member);
}
...
}
Run Code Online (Sandbox Code Playgroud)
最后在拦截器中恢复httpRequest:
public class RestLoggedInterceptorImpl implements Serializable {
@AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
// Recover the context field(s) from superclass:
HttpServletRequest req = ((RestService) ctx.getTarget()).getHttpRequest();
logger.info(req.getRemoteAddr()); // <- this will work now
...
return ic.proceed();
}
...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4432 次 |
| 最近记录: |