Understanding filters and interceptors in JAX-RS
Understanding filters and interceptors in JAX-RS
The default request-response model offered in the JAX-RS implementation fits well for many common use cases. However, at times, you may look at extending the default request-response model. For instance, you may need such extension capabilities while adding support for the custom authentication, customized caching of responses, encoding request content, and so on, without polluting the application code. JAX-RS allows you to do this by adding your own interceptors and filters for both the REST requests and responses, as appropriate.
Typically, filters are used for processing the request-response headers, whereas interceptors are concerned with the marshaling and unmarshaling of the HTTP message bodies. Filters and interceptors can be set on both the client and the server.
Modifying request and response parameters with JAX-RS filters
Implementing server-side request message filters
You can build a request filter for the server by implementing the javax.ws.rs.container.ContainerRequestFilter interface. This filter intercepts the REST API calls and runs before the request invokes the REST resource. You will find this useful for performing authorization checks, auditing requests, or manipulating the request header parameters before invoking the REST API implementation.
The ContainerRequestFilters implementation falls into two categories on the basis of the stage in the request processing cycle that the filters are executed at:
- Postmatching: These filters are executed after identifying the matching Java class resource method for processing the incoming HTTP request (the REST API call). The ContainerRequestFilters implementations, by default, are postmatching (unless you designate them as @PreMatching).
- Prematching: These filters are executed before identifying the matching resource class for a REST API call. Prematching ContainerRequestFilters are designated with the @javax.ws.rs.container.PreMatching annotation.
Postmatching server-side request message filters
These postmatching filters are applied only after a matching Java class resource method has been identified to process the incoming request. As these filters are executed after the resource matching process, it is no longer possible to modify the request in order to influence the resource matching process.
Here is an example of a postmatching server-side request filter. AuthorizationRequestFilter, shown in the following example, ensures that only users with the ADMIN role can access the REST APIs used for configuring the system. The configuration APIs are identified in this example by checking whether the request URI path has the /config/part embedded in it:
The @javax.ws.rs.ext.Provider annotation on the implementation of a filter or an interceptor makes it discoverable by the JAX-RS runtime. You do not need to do any extra configurations for integrating the filters or interceptors.
Prematching server-side request message filters
You can designate ContainerRequestFilters as the prematching filter by annotating with the @javax.ws.rs.container.PreMatching annotation. The prematching filters are applied before the JAX-RS runtime identifies the matching Java class resource method. As this filter is executed before executing the resource matching process, you can use this type of filter to modify the HTTP request contents. Apparently, this can be used for influencing the request matching process, if required.
The following code snippet illustrates how you can use a prematching filter to modify the method type in the incoming HTTP request. This example modifies the method type from POST to PUT. As this is executed before identifying the Java resource method, the change in method type will influence the identification of the Java class resource method for executing the call:
Implementing server-side response message filters
You can build a server-side response filter by implementing the javax.ws.rs.container.ContainerResponseFilter interface. This filter gets executed after the response is generated by the REST API. The response filters, in general, can be used to manipulate the response header parameters present in the response messages. The following example shows how you can use them to modify the response header.
When a REST web client is no longer on the same domain as the server that hosts the REST APIs, the REST response message header should have the Cross Origin Resource Sharing (CORS) header values set to the appropriate domain names, which are allowed to access the APIs. This example uses ContainerResponseFilter to modify the REST response headers to include the CORS header, thus making the REST APIs accessible from a different domain:
Implementing client-side request message filters
JAX-RS lets you build a client-side request filter for REST API calls by implementing the javax.ws.rs.client.ClientRequestFilter interface. This filter is used for intercepting the REST API calls on the client itself.
A very common use case scenario where you may find these filters useful is for checking the accuracy of specific request parameters on the client itself. Even you can abort the call and return the error response object from these filters if the request parameter values are not properly specified.
The following code snippet illustrates how you can use ClientRequestFilter to ensure that all the requests carry the Client-Application header field, which you may use on the server for tracking all clients:
You can register the ClientRequestFilter interface to the client by calling the register() method on the javax.ws.rs.client.Client object. Note that the client object implements the javax.ws.rs.core.Configurable interface, and all the configurations that you see on Client are offered by the Configurable interface. The following code snippet shows how you can add JAXRSClientRequestFilterto the JAX-RS client implementation:
Implementing client-side response message filters
You can build a client-side response filter by implementing the javax.ws.rs.client.ClientResponseFilter interface. This filter is used for manipulating the response message returned by the REST APIs.
The following code snippet shows the use of JAXRSClientResponseFilter to check the response received from the server and then log the error (if any) for audit purposes:
You can register the ClientResponseFilter interface to the JAX RS client by calling the register()method on the javax.ws.rs.client.Client object. An example is given here:
Modifying request and response message bodies with JAX-RS interceptors
JAX-RS provides the request and response interceptors to manipulate entities or message bodies by intercepting the input and output streams, respectively. In this section, we will learn the request and response interceptors offered by JAX-RS. You can use the interceptors on both the client and the server.
Implementing request message body interceptors
You can use the javax.ws.rs.ext.ReaderInterceptor interface to intercept and manipulate the incoming message body.
The following example illustrates how you can implement ReaderInterceptor to intercept the incoming message in order to unzip the zipped body content:
Implementing response message body interceptors
You may use the javax.ws.rs.ext.JAXRSWriterInterceptor interface to intercept and manipulate the outgoing message body. The following example illustrates the use of WriterInterceptor to compress the response body content:
Managing the order of execution for filters and interceptors
Selectively applying filters and interceptors on REST resources by using @NameBinding
The @javax.ws.rs.NameBinding annotation is used for creating the name-binding annotation for filters and interceptors. Later, developers can selectively apply the name-binding annotation on the desired REST resource classes or methods.
For example, consider the following name-binding annotation, RequestLogger:
You can use the preceding name-binding annotation, RequestLogger, to decorate the desired filters and interceptors, as shown here:
Note that the preceding filter is not annotated with the @Provider annotation, and therefore it will not be applied globally on all resources.
As the last step, you can apply the name-binding annotation to the resource class or method to which the name-bound JAX-RS provider(s) should be bound to. In the following code snippet, we apply the @RequestLogger name-binding annotation to the findDepartment() method. At runtime, the framework will identify all filters and interceptors decorated with @RequestLogger and will apply all of them to this method:
Selectively applying filters and interceptors on REST resources by using @NameBinding
The @javax.ws.rs.NameBinding annotation is used for creating the name-binding annotation for filters and interceptors. Later, developers can selectively apply the name-binding annotation on the desired REST resource classes or methods.
For example, consider the following name-binding annotation, RequestLogger:
You can use the preceding name-binding annotation, RequestLogger, to decorate the desired filters and interceptors, as shown here:
Note that the preceding filter is not annotated with the @Provider annotation, and therefore it will not be applied globally on all resources.
As the last step, you can apply the name-binding annotation to the resource class or method to which the name-bound JAX-RS provider(s) should be bound to. In the following code snippet, we apply the @RequestLogger name-binding annotation to the findDepartment() method. At runtime, the framework will identify all filters and interceptors decorated with @RequestLogger and will apply all of them to this method:
Dynamically applying filters and interceptors on REST resources using DynamicFeature
The javax.ws.rs.container.DynamicFeature contract is used by the JAX-RS runtime to register providers, such as interceptors and filters, to a particular resource class or method during application deployment.
The following code snippet shows how you can build the DynamicFeature provider. This example applies RequestLoggerFilter to resource methods annotated with @RequestLogger.
The DynamicFeature interface is executed at deployment time for each resource method. You can register filters or interceptors on the desired resource class or method in the DynamicFeature implementation.
Comments
Post a Comment