Forum Stats

  • 3,769,804 Users
  • 2,253,023 Discussions
  • 7,875,210 Comments

Discussions

Filters in RESTful Java

by Thamizharasu

Filters are one of the important features provided by the JAX-RS framework, and they are used in various contexts when RESTful web services are developed.

Filters are used to modify request and response entities, headers, and other parameters. In this article, we will explore different types of filters and different ways to use them. All the code examples are based on the Jersey framework.

Note: The source code for all the examples in this article is available on GitHub.

Types of Filters

Filters are classified under two major categories: server (container) filters and client filters. These two categories are further divided into request filters and response filters, as shown in Figure 1.

f1.jpg

Figure 1. Types of filters

Container Filters

The following sections describe container filters and an available annotation.

Container Response Filters

When we want to change the response header or modify some parameters after the resource method is executed, we can use a container response filter. For that, we have to provide an implementation of the ContainerResponseFilter interface, as shown in Listing 1.

public class RestSkolResponseFilter implements ContainerResponseFilter {   @Override      public void filter(ContainerRequestContext containerRequestContext,            ContainerResponseContext containerResponseContext) throws IOException {     containerResponseContext.getHeaders().add("X-Powered-By", "RESTSkol");   }}

Listing 1. Example container response filter

This has to be registered as a provider in our resource configuration. In the example in Listing 1, we have injected a parameter called X-Powered-By in the response. (This example is inspired from the Jersey website). In this way, we can easily inject any header value that is common across all our responses.

Container Request Filters

A container request filter is useful when we want to enforce some kind of validation before executing the resource method. So a container request filter will act as a shield for our resource methods. We can enforce any kind of validation, and if the validation fails we can abort the resource execution at that point.

In the example in Listing 2, we are enforcing some kind of validation for the API key in our resource method call. If the expected API key is not available in the request header, then further call execution is stopped and a response is generated from this layer itself.

public class APIKeyCheckRequestFilter implements    ContainerRequestFilter { private static final String API_KEY = "X-API-KEY";    @Override   public void filter(ContainerRequestContext containerRequestContext) throws      IOException { final String apiKey =      containerRequestContext.getHeaders().getFirst(API_KEY); System.out.println("API KEY: " + apiKey);      if (apiKey == null ||           apiKey.isEmpty()) {           containerRequestContext                 .abortWith(                   Response                          .status(Response.Status.UNAUTHORIZED)                 .entity("Please provide a valid API Key")                 .build());     }   }}

Listing 2. Example container request filter

@PreMatching Annotation

By default, the container request filter implementation is executed after the appropriate request execution method is found. Here we will not have control over finding or choosing the required request method.

To achieve this behavior, we have the @PreMatching annotation for container request filters. If any container request filter is annotated with @PreMatching, then we can influence the request method. In the implementation shown in Listing 3, we are delegating the /books/v1 request to the /books/v2 resource. This might be useful when we want to deprecate any of the resource implementations.

@PreMatching public class PreMatchingFilter implements ContainerRequestFilter {   @Override   public void filter(ContainerRequestContext containerRequestContext) throws      IOException { final URI absolutePath =      containerRequestContext.getUriInfo().getAbsolutePath(); String path =      absolutePath.getPath();      System.out.println("Path: " + path);      if (path.contains("/books/v1")) {       path = path.replace("/books/v1", "/books/v2");     }      System.out.println("After replaced Path: " + path);     try {       containerRequestContext.setRequestUri(new URI(path));     } catch (URISyntaxException e) {          e.printStackTrace();     }   }}

Listing 3. Example of using the @PreMatching annotation

Client Filters

Two variants of client filters are available: client request filters and client response filters. These filters are applicable only at the client API layer. This means that when we use the Jersey (JAX-RS) client API to call the REST endpoints, we can add these filters as part of the Client object.

Client Request Filters

This filter is the starting point of the filter workflow. If the client request filter is registered with the Client object, then this will be triggered first before the request has been sent to the server. So we can use this filter when we want to enforce some kind of validation at the client layer itself.

The purpose of the client request filter shown in Listing 4 is to validate when something is calling the PATCH method. We can enforce a validation to prevent such unsupported methods. If this call is not validated successfully, we can abort the request and the complete workflow will come to an end. The request will be passed to the server level once this validation is satisfied.

public class RestSkolClientRequestFilter implements ClientRequestFilter {   private static final Logger logger =    LogManager.getLogger(RestSkolClientRequestFilter.class);   @Override   public void filter(ClientRequestContext clientRequestContext) throws IOException {      logger.info("Client request called");      final String methodName =      clientRequestContext.getMethod(); if      (methodName.equals("PATCH")) {      clientRequestContext.abortWith(          Response.status(Response.Status.METHOD_NOT_ALLOWED)                .entity("HTTP Patch is NOT supported")                .build());     }   }}

Listing 4. Example client request filter

Client Response Filters

A client response filter is the final step in the filter workflow. Once the execution is completed at the server level, the server layer will generate either a successful response or a failure response. This response will be passed to the client layer. A client response filter is the one that will be executed once the response is received from the server. We can validate or inject further entity data into the response at the client response filter level. Then, the response will be passed back to the caller.

How to Use Client Filters

As I already mentioned, client filters (both request and response) should be registered with the Client object, as shown in Listing 5.

final Client client = ClientBuilder.newBuilder()     .register(RestSkolClientRequestFilter.class)     .register(RestSkolClientResponseFilter.class)     .build();

Listing 5. Example of registering client filters

Then these client filters will be enforced during the method call.

Filter Execution Workflow and Output

Figure 2 depicts the complete workflow or steps of the filter execution path.

f2.png

Figure 2. Steps in the filter execution path

The following output shows the execution order that occurs if all of the above filter implementation is in place. Check out the source code and run the project locally to see the following output locally. The output should exact match the order depicted in Figure 2.

[INFO ] 2016-04-12 23:29:38.212 [main] RestSkolClientRequestFilter - Client request called[INFO ] 2016-04-12 23:29:38.509 [http-bio-8080-exec-1] PreMatchingFilter - Pre matching container request filter[INFO ] 2016-04-12 23:29:38.519 [http-bio-8080-exec-1] APIKeyCheckRequestFilter - Container request filter[INFO ] 2016-04-12 23:29:38.530 [http-bio-8080-exec-1] RestSkolResponseFilter - Container response filter[INFO ] 2016-04-12 23:29:38.935 [main] RestSkolClientResponseFilter - Client response filter

Conclusion

The source code for all the examples in this article is available at https://github.com/cloudskol/restskol. Contact me if you need any further details. I will try to answer your questions as much as possible!

See Also

For more detailed information about filters, see the Jersey documentation.

About the Author

Thamizharasu is a Java developer living in Chennai, India. He is enthusiastic about programming and Java EE. Thamizharasu is also involved in JSR activities (JSONB) and currently is serving as a leader of Madras Java User Group. He evangelizes programming and frameworks on his blog and on Twitter.

Join the Conversation

Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!

Comments