JEE6 REST Service @AroundInvoke Interceptor is injecting a null HttpServletRequest object

折月煮酒 提交于 2020-01-12 07:54:05

问题


I have an @AroundInvoke REST Web Service interceptor that I would like to use for logging common data such as the class and method, the remote IP address and the response time.

Getting the class and method name is simple using the InvocationContext, and the remote IP is available via the HttpServletRequest, as long as the Rest Service being intercepted includes a @Context HttpServletRequest in its parameter list.

However some REST methods do not have a HttpServletRequest in their parameters, and I can not figure out how to get a HttpServletRequest object in these cases.

For example, the following REST web service does not have the @Context HttpServletRequest parameter

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

I have tried injecting it directly into my Interceptor, but (on JBoss 6.1) it is always null...

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

I would like advice of a reliable way to access the HttpServletRequest object - or even just the Http Headers ... regardless of whether a REST service includes the parameter.


回答1:


After researching the Interceptor Lifecycle in the Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html I don't think its possible to access any servlet context information other than that in InvocationContext (which is defined by the parameters in the underlying REST definition.) This is because the Interceptor instance has the same lifecycle as the underlying bean, and the Servlet Request @Context must be injected into a method rather than the instance. However the Interceptor containing @AroundInvoke will not deploy if there is anything other than InvocationContext in the method signature; it does not accept additional @Context parameters.

So the only answer I can come up with to allow an Interceptor to obtain the HttpServletRequest is to modify the underlying REST method definitons to include a @Context HttpServletRequest parameter (and HttpServletResponse if required).

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

The interceptor can then iterate through the parameters in the InvocationContext to obtain the 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



回答2:


Another work around to avoid creating additional parameters in every REST method is creating a super class for all REST services that use that kind of interceptors:

public abstract class RestService {
    @Context
    private HttpServletRequest httpRequest;

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

    public HttpServletRequest getHttpRequest() {
        return httpRequest;
    }
}

So the original REST service can extend it without alter any method signature:

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

And finally in the interceptor to recover the 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();
    }
    ...
}



回答3:


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;


来源:https://stackoverflow.com/questions/17519868/jee6-rest-service-aroundinvoke-interceptor-is-injecting-a-null-httpservletreque

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!