02.02.2013
ContainerRequestFilter und ContainerResponseFilter für JAX-RS 2.0
In diesem Teil des Tutorials wird die Filter-Funktionalität von JAX-RS vorgestellt. Bei JAX-RS 2.0 wurden die beiden Interfaces ContainerRequestFilter
und ContainerResponseFilter
eingeführt, durch die es möglich wird, alle Anfragen, die an einen REST-Service gestellt werden, abzufangen. Durch die beiden Interfaces ist es möglich den ContainerRequestContext
oder ContainerResponseContext
zu untersuchen und dann ggf. die Anfrage oder das Ergebnis zu beeinflussen.
Auf der Abbildung sieht man, wie der Client einen Request an den Service /itemService/itemstring des Servers absetzt. Bevor der Request beim REST-Service ankommt, geht er durch einen ContainerRequestFilter. Falls ein Request gefiltert wird, findet eine Umleitung zurück zum Client statt. Bei der Response verhält es sich ähnlich. Bevor die Response an den Client gesendet wird, l�uft sich durch einen ContainerResponseFilter. Falls der Inhalt der Response herausgefiltert wird, liefert der REST-Server ein leeres Ergebnis zur�ck. Ansonsten kommt das Ergebnis Item 1 beim Client an.
Anmerkung: Die Abbildung bezieht sich auf das Beispiel weiter unten. Mit dem ContainerResponseFilter kann auch die Response verändert werden, bevor sie an den Client gesendet wird. Es ist also nicht nur möglich überhaupt kein Ergebnis zu liefern, sondern auch die Antwort zu bearbeiten.
In dem folgenden Beispiel wird bei einem Request anhand eines gesetzten Cookies entschieden, ob ein Ergebnis zurückgeliefert werden soll.
Als erstes wird deshalb eine Klasse TestFilter
im Projekt JAXRSServer
angelegt, die das Interface ContainerRequestFilter
implementiert.
package org.hameister.itemservice;
import java.io.IOException;
import java.util.Map;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
@Provider
public class TestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext crc) throws IOException {
Map<String, Cookie/gt; cookies = crc.getCookies();
if (cookies.containsKey("Filter")) {
Cookie filterCookie = cookies.get("Filter");
String filter = (String) filterCookie.getValue();
if ("f1".equals(filter)) {
//Send "OK", but remove the result.
crc.abortWith(Response.ok().build());
}
}
}
}
In der Methode filter
werden die Cookies des ContainerRequestContext
abgefragt und überfrüft, ob ein Filter
gesetzt wurde und er den Wert f1
hat. Falls dies so ist, wird der Request abgebrochen, liefert aber trotzdem ein ok
als Ergebnis zurück. Es wäre aber auch möglich den Request auf eine andere URL umzuleiten. Beispielsweise mit folgender Anweisung:
crc.abortWith(Response.temporaryRedirect(new URI("/item")).build());
Damit die Klasse nach dem Deployment aufgerufen wird, muss die Annotation @Provider
oberhalb der Klasse vorhanden sein.
Möchte man eine Response filtern, steht das Interface ContainerResponseFilter
zur Verfügung. Hier besteht die Möglichkeit auch auf den ContainerResponseContext
zuzugreifen, um die Ergebnismenge zu überprüfen und ggf. zu filtern.
Im folgenden Beispiel werden beispielsweise alle Objekte von Typ Item
gefiltert, deren Name mit BigItem
beginnt.
package org.hameister.itemservice;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
@Provider
public class TestResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext crc, ContainerResponseContext crc1) throws IOException {
if(crc1.getEntity() instanceof Item) {
Item item = (Item)crc1.getEntity();
if(item.getName().startsWith("BigItem")) {
crc.abortWith(Response.serverError().build());
}
}
}
}
Zum Testen der Methode erweitern wir nun wieder die Klasse JAXRESTClientTest
im Projekt JAXRSClient
um ein paar JUnit-Tests:
public class JAXRESTClientTest {
Client client;
private static final String SERVER = "http://localhost:8080/JAXRSServer/webresources";
public JAXRESTClientTest() {
}
@BeforeClass
public static void setUpClass() {
}
@AfterClass
public static void tearDownClass() {
}
@Before
public void setUp() {
client = ClientFactory.newClient();
}
@After
public void tearDown() {
client.close();
}
...
@Test
public void itemFilter() {
String returnValue = client.target(SERVER + "/itemservice/itemstring").request("text/plain").cookie("Filter", "f1").get(String.class);
Logger.getLogger(JAXRESTClientTest.class.getName()).log(Level.INFO, "Return value ''itemstring'':{0}", returnValue);
Assert.assertEquals("Item was not filtered.", "", returnValue);
}
@Test
public void itemNoFilter() {
String returnValue = client.target(SERVER + "/itemservice/itemstring").request("text/plain").cookie("Filter", "f2").get(String.class);
Logger.getLogger(JAXRESTClientTest.class.getName()).log(Level.INFO, "Return value ''itemstring'':{0}", returnValue);
Assert.assertEquals("Item was not filtered.", "Item 1", returnValue);
}
}
Der erste Test setzt einen Filter f1
und erwartet daraufhin, dass kein Ergebnis zurückgeliefert wird. Beim zweiten Test wird ein Filter f2
gesetzt, der laut Filter-Implementierung, keine Wirkung hat. Deshalb liefert der REST-Service ein Ergebnis zurück, dessen Wert überprüft werden kann.
Die oben erstellten Filter greifen für jeden Request und jede Response. Oft ist es aber gewünscht, dass nur bei bestimmten Aufrufen ein Filter aktiv ist. Beispielsweise nur für eine einzelne Methode des REST-Service. Wie dies funktioniert, wird im folgenden Teil des Tutorials beschrieben.