In the dynamic world of web development, managing incoming requests efficiently is paramount for robust and user-friendly applications. One common requirement is the ability to manipulate or rewrite URLs on the fly, perhaps for search engine optimization (SEO), maintaining clean URLs, or handling legacy links. While server-level configurations like Apache’s mod_rewrite or Nginx’s rewrite module are powerful, sometimes you need more granular control directly within your Java web application. This is where the humble yet mighty Servlet Filter comes into play. Learning how to use a Servlet Filter to change/rewrite an incoming URL provides a flexible and programmatic way to intercept requests, modify their perceived destination, and ensure your application handles them precisely as intended, enhancing both user experience and maintainability.
Understanding Servlet Filters and URL Rewriting
A Servlet Filter is a Java EE component that allows developers to intercept requests and responses before and after they reach or leave a servlet. Think of it as a gatekeeper in your application’s request processing pipeline. Filters are configured in the web.xml deployment descriptor or through annotations, enabling them to execute code for specific URL patterns. This interception capability makes them ideal for tasks such as logging, authentication, compression, character encoding, and, crucially, URL rewriting.
URL rewriting itself is the process of transforming a URL from one format to another. For instance, you might want to change a dynamic URL like /product?id=123 into a more human-readable and SEO-friendly /product/123/fancy-widget. This not only makes URLs easier to remember and share but also helps search engines understand the content better. Beyond SEO, URL rewriting can be essential for maintaining backward compatibility when your application’s URL structure changes, or for implementing a front controller pattern where all requests are routed through a single entry point.
When a request hits your web server, it first passes through any configured filters before reaching the target servlet. This positioning in the request processing pipeline is key. A Servlet Filter can examine the incoming request’s URL, decide if it needs modification, and then forward a “modified” version of the request down the chain. This doesn’t literally change the URL in the browser’s address bar (that requires a redirect), but it changes how your application perceives and processes the request, directing it to a different internal resource.
To effectively change or rewrite an incoming URL using a Servlet Filter, you cannot directly modify the HttpServletRequest object itself, as its methods for retrieving URL information (like getRequestURI() or getRequestURL()) are read-only. The solution lies in the HttpServletRequestWrapper class. This class provides a convenient way to “wrap” an existing request object and override specific methods to provide custom behavior, all while delegating other calls to the original request.
When you create a custom HttpServletRequestWrapper, you typically extend it and then override methods like getRequestURI(), getRequestURL(), getServletPath(), or getPathInfo() to return your desired rewritten URL components. For instance, if you want to internally route /old-path to /new-path, your wrapper would provide /new-path when getRequestURI() is called. The doFilter method of your Servlet Filter is where you’ll instantiate this wrapper and pass it down the filter chain using chain.doFilter(wrappedRequest, response);.
For example, if a user requests /products/12345, and your application internally expects /displayProduct?id=12345, your HttpServletRequestWrapper would intercept the call to getRequestURI(), parse out “12345”, and construct the internal URI. This approach ensures that all subsequent components in the request processing pipeline, including servlets, JSP pages, and other filters, see the rewritten URL, not the original one. This pattern is widely used in frameworks and custom applications for implementing clean URL strategies without requiring server-level rewrite rules, offering a highly portable solution.
Implementing Your Custom URL Rewriting Filter
Implementing a URL rewriting filter involves creating a custom filter class and then configuring it. Hereβs a step-by-step guide to achieving this:
-
Create Your Custom HttpServletRequestWrapper
Define a class that extends javax.servlet.http.HttpServletRequestWrapper. In this class, you will override the methods responsible for returning URL information, such as getRequestURI(), getRequestURL(), and potentially getServletPath() or getPathInfo(), to return your rewritten path. You’ll need a constructor that accepts the original HttpServletRequest and the new URI.
public class RewrittenRequestWrapper extends HttpServletRequestWrapper { private String newUri; public RewrittenRequestWrapper(HttpServletRequest request, String newUri) { super(request); this.newUri = newUri; } @Override public String getRequestURI() { return newUri; } @Override public StringBuffer getRequestURL() { StringBuffer url = super.getRequestURL(); return new StringBuffer(url.toString().replace(super.getRequestURI(), newUri)); } } -
Develop Your Servlet Filter
Create a class that implements javax.servlet.Filter. Implement the doFilter method. Inside doFilter, you’ll inspect the incoming request’s URI, apply your rewriting logic, create an instance of your RewrittenRequestWrapper with the new URI, and pass this wrapped request down the chain.
public class UrlRewriteFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // Initialization code if needed } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; String originalUri = httpRequest.getRequestURI(); String contextPath = httpRequest.getContextPath(); String pathWithoutContext = originalUri.substring(contextPath.length()); String newUri = originalUri; // Default to original // Example Rewrite Logic: /products/12345 to /displayProduct?id=12345 if (pathWithoutContext.matches("/products/\\d+")) { String productId = pathWithoutContext.substring(pathWithoutContext.lastIndexOf("/") + 1); newUri = contextPath + "/displayProduct?id=" + productId; System.out.println("Rewriting " + originalUri + " to " + newUri); chain.doFilter(new RewrittenRequestWrapper(httpRequest, newUri), response); return; // Important: prevent original request from continuing } else if (pathWithoutContext.equals("/about-us")) { newUri = contextPath + "/static/about.jsp"; // Example: clean URL to internal JSP System.out.println(" <b>Question & Answer : </b><br></br><p>How can I use a Servlet Filter to change an incoming URL from</p> <p>http://nm-java.appspot.com/Check_License/Dir_My_App/Dir_ABC/My_Obj_123</p> <p>to</p> <p>http://nm-java.appspot.com/Check_License?Contact_Id=My_Obj_123</p> <p>?</p> <hr></hr> <p><strong>Update</strong>: according to BalusC's steps below, I came up with the following code:</p> public class UrlRewriteFilter implements Filter { @Override public void init(FilterConfig config) throws ServletException { // } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; String requestURI = request.getRequestURI(); if (requestURI.startsWith("/Check_License/Dir_My_App/")) { String toReplace = requestURI.substring(requestURI.indexOf("/Dir_My_App"), requestURI.lastIndexOf("/") + 1); String newURI = requestURI.replace(toReplace, "?Contact_Id="); req.getRequestDispatcher(newURI).forward(req, res); } else { chain.doFilter(req, res); } } @Override public void destroy() { // } } <p>The relevant entry in web.xml look like this:</p> <filter> <filter-name>urlRewriteFilter</filter-name> <filter-class>com.example.UrlRewriteFilter</filter-class> </filter> <filter-mapping> <filter-name>urlRewriteFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <p>I tried both server-side and client-side redirect with the expected results. It worked, thanks BalusC!</p> <br></br><ol> <li>Extend <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/http/httpfilter" rel="nofollow noreferrer">jakarta.servlet.http.HttpFilter</a>.</li> <li>In <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/http/httpfilter#doFilter(jakarta.servlet.http.HttpServletRequest,jakarta.servlet.http.HttpServletResponse,jakarta.servlet.FilterChain)" rel="nofollow noreferrer">doFilter()</a> method, use <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/http/httpservletrequest#getRequestURI()" rel="nofollow noreferrer">HttpServletRequest#getRequestURI()</a> to grab the path.</li> <li>Use straightforward <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html" rel="nofollow noreferrer">java.lang.String</a> methods like <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#substring(int)" rel="nofollow noreferrer">substring()</a>, <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#split(java.lang.String)" rel="nofollow noreferrer">split()</a>, <a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html#concat(java.lang.String)" rel="nofollow noreferrer">concat()</a> and so on to extract the part of interest and compose the new path.</li> <li>Use either <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/servletrequest#getRequestDispatcher(java.lang.String)" rel="nofollow noreferrer">ServletRequest#getRequestDispatcher()</a> and then <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/requestdispatcher#forward(jakarta.servlet.ServletRequest,jakarta.servlet.ServletResponse)" rel="nofollow noreferrer">RequestDispatcher#forward()</a> to forward the request/response to the new URL (server-side redirect, not reflected in browser address bar), <strong>or</strong> use <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/http/httpservletresponse#sendRedirect(java.lang.String)" rel="nofollow noreferrer">HttpServletResponse#sendRedirect()</a> to redirect the response to the new URL (client side redirect, reflected in browser address bar).</li> <li>Register the filter via the <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/annotation/webfilter" rel="nofollow noreferrer">@WebFilter</a> annotation on an url-pattern of /* or /Check_License/*, depending on the context path.</li> </ol> <p>Don't forget to add a check in the code if the URL <em>needs</em> to be changed and if <em>not</em>, then just call <a href="https://jakarta.ee/specifications/platform/10/apidocs/jakarta/servlet/filterchain#doFilter(jakarta.servlet.ServletRequest,jakarta.servlet.ServletResponse)" rel="nofollow noreferrer">FilterChain#doFilter()</a>, else it will call itself in an infinite loop.</p> <p>Alternatively you can also just use an existing 3rd party API to do all the work for you, such as <a href="https://tuckey.org/urlrewrite/" rel="nofollow noreferrer">Tuckey's UrlRewriteFilter</a> which can be configured the way as you would do with Apache's mod_rewrite.</p>