2013-07-08 18 views
5

Ich habe einen @AroundInvoke-REST-Web-Service-Interceptor, den ich für die Protokollierung allgemeiner Daten wie Klasse und Methode, Remote-IP-Adresse und Antwortzeit verwenden möchte.JEE6-REST-Dienst @AroundInvoke Interceptor injiziert ein Null-HttpServletRequest-Objekt

Das Abrufen des Klassen- und Methodennamens ist mit InvocationContext einfach, und die Remote-IP ist über den HttpServletRequest verfügbar, solange der abgefangene Restdienst einen @Context HttpServletRequest in seiner Parameterliste enthält.

jedoch einige REST-Methoden haben keine HttpServletRequest in ihren Parametern, und ich kann nicht herausfinden, wie ein HttpServletRequest Objekt in diesen Fällen zu erhalten.

Zum Beispiel die folgenden REST Web-Service nicht über die @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); 
} 

Ich habe versucht, es direkt in mein Interceptor Injektion, sondern (auf JBoss 6.1) ist es immer 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(); 

würde Ich mag Rat eines zuverlässigen Weg, um das HttpServletRequest Objekt zuzugreifen - oder auch nur die HTTP-Header ... unabhängig davon, ob ein REST-Service umfasst den Parameter.

Antwort

4

Nach der Erforschung der Interceptor Lifecycle in der Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html Ich denke nicht, es ist möglich, auf andere Servlet-Kontextinformationen als in InvocationContext zugreifen (die durch die Parameter in der zugrunde liegenden REST-Definition definiert ist.) Dies ist, weil der Interzeptor Instanz hat denselben Lebenszyklus wie die zugrunde liegende Bean, und die Servlet-Anforderung @Context muss in eine Methode und nicht in die Instanz eingefügt werden. Der Interceptor, der @AroundInvoke enthält, wird jedoch nicht bereitgestellt, wenn in der Methodensignatur etwas anderes als InvocationContext vorhanden ist. Es akzeptiert keine zusätzlichen @Context-Parameter.

Die einzige Antwort, die ich bekommen kann, damit ein Interceptor den HttpServletRequest erhalten kann, besteht darin, die zugrundeliegende REST-Methode mit einem @Context-HttpServletRequest-Parameter (und ggf. HttpServletResponse) zu modifizieren.

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

Der Interceptor kann dann durch die Parameter in der InvocationContext iterieren die 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

Bedeutet dies bedeutet, dass Sie eine @Context HttpServletRequest enthalten haben Parameter in jeder REST-Methode Ihrer Anwendung? Es muss ein besserer Weg sein. Ich bin ziemlich neu in Java EE6 und ich kämpfe mit einem sehr ähnlichen, wenn nicht das gleiche Problem. Ich habe hier eine Frage geschrieben, es fragt nicht dasselbe, aber es kommt darauf an: Ich muss die Sitzung auf dem Abfangjäger überprüfen, um eine Autorisierung durchzuführen. http://stackoverflow.com/questions/19453557/injection-to-an-interceptor-whats-missing – noinstance

+0

@nosuchnick Ich dachte auch "es muss einen besseren Weg geben", deshalb habe ich die Frage gestellt. Dies scheint einer dieser JEE6-kleinen Ärgernisse zu sein, bei denen das Hinzufügen eines Interceptors Änderungen am abgefangenen Code erfordert. –

+0

Sicher. Am Ende habe ich die gesamte Session-Verwaltung in einer '@ SessionScoped'-Bean gemacht und sie in den Interceptor injiziert. Ich kann dann seine Methoden dort anwenden. – noinstance

0

Ich bin mit Glassfish 3.1.2 zu erhalten.

2 Jersey

Für HTTP-Header für mich funktioniert:

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

Um UriInfo Sie das tun können:

@Inject 
@Context 
private UriInfo uriInfo; 
4

Eine weitere Arbeit um zusätzliche Parameter in jedem REST-Methode zu vermeiden, die Schaffung schafft eine Superklasse für alle REST-Services, die diese Art von Abfangjäger verwenden:

public abstract class RestService { 
    @Context 
    private HttpServletRequest httpRequest; 

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

    public HttpServletRequest getHttpRequest() { 
     return httpRequest; 
    } 
} 

So der ursprüngliche REST-Service verlängern kann es ohne jede Methode Signatur ändern:

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

Und schließlich in der Interceptor Httprequest zu erholen:

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