2013-07-08 5 views
5

У меня есть перехватчик @AroundInvoke REST Web Service, который я бы хотел использовать для регистрации общих данных, таких как класс и метод, удаленный IP-адрес и время отклика.JEE6 REST Service @AroundInvoke Interceptor вводит нулевой объект HttpServletRequest

Получение имени класса и метода простым использованием InvocationContext, а удаленный IP-адрес доступен через HttpServletRequest, если перехват службы Rest включает в себя список @Context HttpServletRequest в списке параметров.

Однако некоторые методы REST не имеют HttpServletRequest в своих параметрах, и я не могу понять, как получить объект HttpServletRequest в этих случаях.

Например, следующий REST веб-службы не имеет параметр @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-заголовки ... независимо от того, служба REST, включает ли параметр.

ответ

4

После изучения жизненного цикла перехватчика в Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html я не думаю, что его можно получить доступ к любой информации контекста сервлета, отличной от информации в InvocationContext (которая определяется параметрами в базовом определении REST.) Это связано с тем, что Interceptor экземпляр имеет тот же жизненный цикл, что и базовый компонент, а запрос Servlet @Context должен быть введен в метод, а не экземпляр. Однако Interceptor, содержащий @AroundInvoke, не будет разворачиваться, если в сигнатуре метода есть что-то иное, кроме InvocationContext; он не принимает дополнительные параметры @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 
+0

ли это означает, что вы должны включать в себя @Context HttpServletRequest параметр в каждом методе REST вашего приложения? Это должен быть лучший способ. Я довольно новичок в Java EE6, и я борюсь с очень похожей, если не той же проблемой. Я написал здесь вопрос, он не спрашивает то же самое, но это сводится к следующему: мне нужно проверить сеанс на перехватчике, чтобы выполнить авторизацию. http://stackoverflow.com/questions/19453557/injection-to-an-interceptor-whats-missing – noinstance

+0

@nosuchnick Я также подумал: «Должен быть лучший способ», поэтому я задал вопрос. Кажется, это один из тех немногих раздражающих JEE6, где добавление перехватчика требует изменений шаблонов к перехваченному коду. –

+0

Несомненно. Я закончил все управление сеансом в компоненте '@ SessionScoped' и ввел его в перехватчик. Затем я могу использовать его методы. – noinstance

0

Я использую Glassfish 3.1.2.2 Джерси

Для заголовка HTTP это работает для меня:

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

Чтобы получить UriInfo вы можете сделать это:

@Inject 
@Context 
private UriInfo uriInfo; 
4

другой работы вокруг, чтобы избежать создания дополнительных параметров в каждом методе 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(); 
    } 
    ... 
}