JAX-RS Providers Part II
Last updated
Last updated
Let's continue exploring different JAX-RS providers and dive into
Filters: one of the foremost components of the JAX-RS request processing chain
Filters provide AOP (Aspect Oriented Programming) like capabilities within JAX-RS applications and allow developers to implement cross cutting application specific concerns which ideally should not be sprinkled all over the business logic e.g. authentication, authorization, request/response validation etc. The AOP-based programming model involves interposing on methods of JAX-RS resource classes and dealing with (or mutating) components of HTTP request/response - headers, request URIs, the invoked HTTP method (GET, POST etc)
Server side request filters act on incoming HTTP requests from the clients and provide an opportunity to act on/make decisions based on certain characteristics of the HTTP request. In order to implement a server side request filter, one needs to implement the javax.ws.rs.container.ContainerRequestFilter
interface (which is an extension provided by JAX-RS). An instance of the javax.rs.ws.ContainerRequestContext
interface is seamlessly injected by the container into the filter method of ContainerRequestFilter
. It is a mutable object (on purpose) and exposes methods to access and modify HTTP request components
A JAX-RS request processing pipeline involves dispatching a HTTP request to the appropriate Java method in the resource classes based on matching algorithm implemented by the JAX-RS provider. Filters take this into account and are divided into pre and post matching filters
As the name indicates, Pre-matching filters are executed before the incoming HTTP request is mapped/dispatched to a Java method. Use the javax.ws.rs.container.PreMatching
annotation on the filter implementation class
A Post-matching filter is executed by the JAX-RS container only after the completion of method dispatch/matching process. Unlike, pre-matching filters, these filters do not need an explicit annotation i.e. filter classes without the @PreMatching
annotation are assumed to be post-matching by default
A JAX-RS application can have multiple filters (in a chain like structure) which are executed as per user defined order (more on this later) or a default one (container driven). However, it possible to break the chain of processing by throwing an exception from the filter implementation logic or by calling the
abortWith
method. In either cases, the other request filters in the chain are not invoked and the control is passed on to the Response Filters (if any).
A server side Response filter is invoked by the runtime after a response (or an exception) is generated by the JAX-RS resource method (before dispatching the same to the caller/client). Response filters are similar to their counterparts (Request filters) in terms of their utility (read/mutate aspects of the response e.g. HTTP headers) and programming model (executed as a chain in a user defined or default order) Setting up a server side response filter is as simple as providing an implementation for the javax.ws.rs.container.ContainerResponseFilter
interface. The injection of ContainerResponseContext
into the filter method of the ContainerResponseFilter
interface is taken care of by the JAX-RS runtime
Response filters also need to be annotated with the
javax.ws.rs.ext.Provider
annotation in order for the JAX-RS runtime to recognize it automatically.
Client side request filters are invoked after the HTTP request creation before it is dispatched to the server. They can be to mutate/make decisions based on the properties of the HTTP request (Headers, Cookies etc) In order to implement a client side request filter, one needs to implement the extension interface provided by JAX-RS - javax.ws.rs.client.ClientRequestFilter
Client side response filters are invoked after the HTTP response has been received from the server end but before it is dispatched to the caller/client. It provides an opportunity to mutate the properties of the HTTP response (Headers, Cookies etc) In order to implement a client side response filter, one needs to implement the extension interface provided by JAX-RS - javax.ws.rs.client.ClientResponseFilter
JAX-RS API enables sharing of user-defined data amongst filters associated with a particular request
It is abstracted in the form of a Map (pretty natural choice) via the ContainerRequestContext interface
Get all the custom properties using the getPropertyNames()
method
The value of a specific property can be fetched (from the Map
) using getProperty(String name)
Overwrite an existing property or a add a new one using setProperty(String name, Object val)
The same capability is available in the Client side JAX-RS filters as well. The only difference is that you would be interacting with an instance of the ClientRequestContext
{title="",lang=java}
Interceptors are similar to filters in the sense that they are also used to mutate HTTP requests and responses, but the major difference lies in the fact that Interceptors are primarily used to manipulate HTTP message payloads. They are divided into two categories - javax.ws.rs.ext.ReaderInterceptor
and javax.ws.rs.ext.WriterInterceptor
for HTTP requests and responses respectively.
The same set of interceptors are applicable on the client side as well (unlike filters)
A ReaderInterceptor
is a contract (extension interface) provided by the JAX-RS API. On the server side, a Reader Interceptor acts on HTTP payloads sent by the client while the client side reader interceptors are supposed to act on (read/mutate) the request payload prior to it being sent to the server
On the server side, a WriterInterceptor
act on HTTP payloads produced by the resource methods while the client side writer interceptors are supposed to act on (read/mutate) the payload sent by the server prior to it being dispatched to the caller
Interceptors are invoked in a chain like fashion (similar to filters). They are only triggered when entity providers (
MessageBodyReader
andMessageBodyWriter
) are required to convert HTTP message to and from their Java object representations. BothReaderInterceptor
andWriterInteceptor
wrap aroundMessageBodyReader
andMessageBodyWriter
respectively and hence executed in the same call stack.
JAX-RS 2.0 defines multiple ways using which server side filters and interceptors can be bound to their target components.
Global Binding
Named Binding
Dynamic Binding
By default, JAX-RS filters and interceptors are bound to all the methods of resource classes in an application. That is, both request (pre and post) and response filters will be invoked whenever any resource method is invoked in response to a HTTP request by the client. This convention can be overridden using named binding or dynamic binding.
Filters and interceptors scoping can be handled in a fine-grained manner (based on per resource class/method)
Configuring Named Binding
If it is applied to a class, the filter/interceptor will be bound to all its resource methods
JAX-RS provides the DynamicFeature
interface to help bind filters and interceptors dynamically at runtime. They can be used in tandem with the more static way of binding made possible using @NamedBinding
. The injected instance of the ResourceInfo
interface helps you choose the resource method in dynamic fashion by exposing various methods and the FeatureContext
interface allows us to register the filter or interceptor once the resource method has been selected.