6

我有一个 @AroundInvoke REST Web 服务拦截器,我想用它来记录常见数据,例如类和方法、远程 IP 地址和响应时间。

使用 InvocationContext 获取类和方法名很简单,远程 IP 可以通过 HttpServletRequest 获得,只要被拦截的 Rest Service 在其参数列表中包含 @Context HttpServletRequest 即可。

然而,一些 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 服务是否包含该参数。

4

3 回答 3

5

在研究了 Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html中的拦截器生命周期之后,我认为除了在InvocationContext(由底层 REST 定义中的参数定义)这是因为 Interceptor 实例与底层 bean 具有相同的生命周期,并且必须将 Servlet Request @Context 注入到方法而不是实例中。但是,如果方法签名中存在 InvocationContext 以外的任何内容,则包含 @AroundInvoke 的拦截器将不会部署;它不接受额外的@Context 参数。

因此,我能想出的允许拦截器获取 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
于 2013-07-08T07:23:50.757 回答
5

另一个避免在每个 REST 方法中创建额外参数的解决方法是为所有使用这种拦截器的 REST 服务创建一个超类:

public abstract class RestService {
    @Context
    private HttpServletRequest httpRequest;

    // Add here any other @Context fields & associated getters 

    public HttpServletRequest getHttpRequest() {
        return httpRequest;
    }
}

因此原始 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);
    }
    ...
}

最后在拦截器中恢复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();
    }
    ...
}
于 2016-02-11T22:45:26.883 回答
0

I'm using Glassfish 3.1.2.2 Jersey

For http header this works for me:

@Inject
@HeaderParam("Accept")
private String acceptHeader;

To get UriInfo you can do this:

@Inject
@Context
private UriInfo uriInfo;
于 2014-07-29T13:54:08.357 回答