Write your own URL rewrite filter for existing JSF pages

This blog is based on our simple URL rewrite implementation for our existing JSF/PrimeFaces pages. As a demonstration, suppose you have an existing page with following URL pattern.

http://tld.com/myapp/store_page.xhtml?item=value&sub_item=value2

The assumption is store_page.xhtml is a page template that is populated by data based on the query parameters (item=value, etc.) that is drawn somewhere most probably from a database. We wanted it to be rewritten to be descriptive and some may even say, SEO friendly URL like below.

http://tld.com/myapp/store/specials/value_item_description

Our filter below processes both the original URL and the rewritten URL. In case of the original URL, we rebuild the URL to the new pattern and resubmit the page through 301 redirect. Please see inline comments for more information.

The filter

public class UrlRewriteFilter implements Filter {

    public UrlRewriteFilter () {
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest srequest = (HttpServletRequest) request;
        HttpServletResponse sresponse = (HttpServletResponse) response;

        String url = srequest.getRequestURI().trim();

        // Process the written URL in the form
        // http://tld.com/myapp/store/specials/value_item_description
        // Forward to the original page silently without
        // the knowledge of the browser, URL displayed in
        // browser remains the same.
		//
		// In our case, before we forward the page
		// we first make sure the descriptive text part
		// of the URL was not changed by comparing it
		// against the database record and 
		// do a redirect if it was changed and then 
		// takes us back here eventually.
 
        if (url.contains("store/specials")) {
            StringBuilder forward = new StringBuilder();                        
            forward.append("/store.xhtml?");
            forward.append(srequest.getQueryString());
            request.getRequestDispatcher(forward.toString()).forward(request, response);
            
        // Process access to the original URL in the form 
        // http://tld.com/myapp/store_page.xhtml?item=value&subitem=value2  
        // This takes another loop to this filter but to the if part of this
        // block.  The browser is aware of the redirect and URL displayed
        // in the browser is rewritten.
            
        } else if (url.contains("/store_page.xhtml")) {
            sresponse.setStatus(301);
            sresponse.sendRedirect("http://tld.com/myapp/" + rebuildUrl(srequest));
        } else {            
            chain.doFilter(request, response);
        }

    }
    
    String rebuildUrl (HttpServletRequest srequest) {
        
        final String itemValue = srequest.getParameter("item");
        final String subItemValue = srequest.getParameter("sub_item");        
        String description = "retrieve value from database or some where else";
        
        // In our case, we included itemValue to the rewritten
        // URL because itemValue is the key to our database. We retrieve 
        // page data based on itemValue.

        description = itemValue+"-"+
	      subItemValue+"-" +
              description.replaceAll("[^a-z0-9]", "_");
        
        // replaceAll above replaces all non alpha-numeric characters in
        // the description with an underscore.  
               
        return description;
    }

 
    @Override
    public void destroy() {
        // your code
    }

    @Override
    public void init(FilterConfig filterConfig) { 
        // your code
    }

}

However you build your new rewritten URL, the important thing to note is that you are able to retrieve back the record with your new URL pattern. In the example in this blog, key to the record is embedded in the URL itself. Splitting the query string gives us the needed data.

In the web.xml of your application, please don't forget to add the following. It should have been added already by the IDE you are using.

<filter>
    <filter-name>UrlRewrite</filter-name>
    <filter-class>PACKAGE.UrlRewrite</filter-class>
</filter>
<filter-mapping>
    <filter-name>UrlRewrite
    <url-pattern>/*
</filter-mapping>

Also note that our filter applies to all pages of our application as indicated in line 7 of the filter mapping. You may also want to change that so that your filter will only process certain pages.

About the forwarded page

Depending on the filter mapping, your filter will process every single references to links in your page and will activate the filter each time. References to javascript, CSS, images should include your app name or should be fully qualified. See below for example.

<link type="text/css" rel="stylesheet" href="./css/layout.css" />

Should be change to

<link type="text/css" rel="stylesheet" href="/myapp/css/layout.css" />

Canonical

With the rewritten URL, you just added a new page with the same content as the original page. Page duplicates are not very SEO friendly. To remedy the situation add the following in the head section of your page.

 <link rel="canonical" 
		href="http://tld.com/myapp/store/specials/value_item_description" />

In our case, we did it from the backing code of the page shown below.

 <link rel="canonical" href="#{stormBean.canonical}" />

Where href above is your new rewritten URL.

If you maintain a sitemap.xml, changes to your URL pattern should be reflected there as well.

That's it, good luck.

If you like the article, please share.
(Site URL pattern has changed as a result social actions counter was reset.)



Comment icon   Comments (Newest first)