Filters in RESTful Java

Version 6

    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!