Bassem 21/07
In questo articolo vediamo come intercettare richieste e risposte utilizzando i filtri in Quarkus. Analizziamo come implementarli in uno stack tradizionale ed in uno reattivo.
In questo esempio, costruiamo due rest endpoint, uno utilizzando la tradizionale dipendenza resteasy e l'altro utilizzando web-vertex, in entrambi i casi implementiamo filtri per poter agire sulle richieste e risposte. In particolare, aggiungiamo dei 'header' alla risposta.
In questa sezione, esaminiamo le annotazioni e le interfacce che vengono utilizzate in seguito nel codice per raggiungere il nostro obiettivo.
Per il 'servelt container' viene utilizzata l'interfaccia ContainerResponseFilter
per intercettare la risposta e agire su di essa prima di raggiungere il nostro utente finale. Nel caso in cui vogliamo gestire la richiesta, possiamo implementare il ContainerRequestFilter
.
Per il contesto reattivo, usiamo l'annotazione @RouteFilter
, che viene messa su un metodo void con un parametro di tipo RoutingContext
. Il metodo contiene la logica che viene applicata a un determinato percorso.
Per prima cosa diamo un'occhiata al livello del controller:
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello QUARKUS";
}
}
È un semplice endpoint che restituisce una stringa, se docessi restituire un json dovresti aggiungere una libreria json al tuo pom.xml, si può scegliere tra la dipendenza RESTEasy Jackson o RESTEasy JSON-B.
Il secondo controller è di tipo reattivo:
@ApplicationScoped
public class GreetingRoute {
@Route(path = "reactive", methods = HttpMethod.GET)
public void reactive(RoutingContext ctx) {
ctx.response().end("Hello Reactive QUARKUS");
}
}
Anche qui, è un metodo get che restituisce una stringa. Abbiamo usato l'annotazione CDI @ApplicationScoped
, cioè questo bean viene creato solo quando viene invocato.
Ora diamo una occhiata al Jax-Filter:
@Provider
public class JaxFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Content-Type-Options", "nosniff");
}
}
In questa classe stiamo implementando l'interfaccia ContainerResponseFilter
. Sovrascriviamo il metodo filter
ed al suo interno aggiungiamo uno nuovo 'header' ad ogni risposta. Il X-Content-Type-Options
con valore nosniff
è un ''header di sicurezza che aiuta ad impedire un attacco MIME sniffing.
Questo filtro non viene applicato alle rotte reattive, ma solo a quelle servlet.
L'ultimo filtro che andiamo ad esaminare è quello reattivo:
public class VertexFilter {
@RouteFilter
void myFilter(RoutingContext rc) {
rc.response().putHeader("X-Header", "Vertex header");
rc.next();
}
}
In questo caso non è necessario annotare la classe con alcuna annotazione CDI, Quarkus seleziona automaticamente il filtro poiché stiamo utilizzando l'annotazione @RouteFilter
. In questo filtro stiamo semplicemente aggiungendo un 'header' personalizzato alla risposta.
Questo filtro verrà applicato a tutti gli endpoint della nostra applicazione, tradizionali e reattivi.
Eseguendo il seguente test:
@QuarkusTest
class GreetingResourceTest {
@Test
void testHelloEndpoint() {
given()
.when().get("/hello")
.prettyPeek()
.then()
.statusCode(200)
.header("X-Content-Type-Options", "nosniff");
}
}
Abbiamo il seguente output:
HTTP/1.1 200 OK
X-Header: Vertex header
Content-Length: 13
X-Content-Type-Options: nosniff
Content-Type: text/plain;charset=UTF-8
Hello QUARKUS
Come si può vedere i due filtri sono stati applicati e abbiamo i due 'header' X-Header
e X-Content-Type-Options
.
Ora eseguendo il secondo test:
@QuarkusTest
class GreetingRouteTest {
@Test
void testHelloEndpoint() {
given()
.when().get("/reactive")
.prettyPeek()
.then()
.statusCode(200)
.header("X-Header", "Vertex header");
}
}
Abbiamo la seguente risposta:
HTTP/1.1 200 OK
X-Header: Vertex header
content-length: 22
Hello Reactive QUARKUS
Da notare che abbiamo solo l'intestazione X-Header
.
Tutto qua, spero che lo troviate utile. Il codice della completa applicazione può essere trovato su GitHub.