Creating RESTful Urls with Spring MVC 3.1 Part Three: UrlRewriteFilter

This blog entry describes how you can create RESTful url addresses to your web application with Spring MVC 3.1 and UrlRewriteFilter. In other words, the url addresses of your application must fulfill following requirements:

  • An url address must have a suffix in it (in other words, an url address most not contain a suffix like '.action').
  • The context path of the web application which is visible to the users of the application must not start with a prefix like 'app'.

It is time to find out how you can fulfill the given requirements.

Required Steps

The steps required to fulfill the given requirements with Spring MVC 3.1 and UrlRewriteFilter are following:

  • Configuring the dispatcher servlet and UrlRewriteFilter
  • Implementing the web application

These steps are described with more details in following.

Configuring the Dispatcher Servlet and the UrlRewriteFilter

The first step is to configure the dispatcher servlet and UrlRewriteFilter. This step consist of two phases:

  1. Map the dispatcher servlet to url pattern '/app/*' and the UrlRewriteFilter to url pattern '/*'.
  2. Configure the UrlRewriteFilter to hide the url pattern of dispatcher servlet.

First, you have to configure you web application. If you are using Spring 3.1 and Servlet 3.0, you can do this by implementing the WebApplicationInitializer interface. Otherwise you will have to create a web.xml configuration file which contains the same configuration (This is left as an exercise for the reader). The source code of my web application configuration class is given in following:

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter;

import javax.servlet.*;

public class RestfulInitializer implements WebApplicationInitializer {
    
    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
    private static final String DISPATCHER_SERVLET_MAPPING = "/app/*";

    private static final String URL_REWRITE_FILTER_NAME = "urlRewrite";
    private static final String URL_REWRITE_FILTER_MAPPING = "/*";
    private static final String URL_REWRITE_FILTER_PARAM_LOGLEVEL = "logLevel";
    private static final String URL_REWRITE_FILTER_LOGLEVEL_SLF4J = "slf4j";
    
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(ApplicationContext.class);

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);

        servletContext.addListener(new ContextLoaderListener(rootContext));

        FilterRegistration.Dynamic urlReWrite = servletContext.addFilter(URL_REWRITE_FILTER_NAME, new UrlRewriteFilter());
        urlReWrite.setInitParameter(URL_REWRITE_FILTER_PARAM_LOGLEVEL, URL_REWRITE_FILTER_LOGLEVEL_SLF4J);
        EnumSet<DispatcherType> urlReWriteDispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
        urlReWrite.addMappingForUrlPatterns(urlReWriteDispatcherTypes, true, URL_REWRITE_FILTER_MAPPING);
    }
}

Second, you must configure the UrlRewriteFilter properly. The web application root of my example application has following directory structure:

  • Static resources such as css files are located under static folder.
  • JSP files are located under WEB-INF/jsp folder.

The UrlRewriteFilter can be configured by creating a configuration file called urlrewrite.xml to WEB-INF folder. The contents of this file is given in following:

<urlrewrite default-match-type="wildcard">
    <!-- All requests to root are forwarded to home page. -->
    <rule>
        <from>/</from>
        <to last="true">/app/home</to>
    </rule>
    <!-- Ensures that requests are forwarded to dispatcher servlet. -->
    <rule match-type="regexp">
        <!--
            Processes only requests which are not:
            1) Requests made for obtaining static resources.
            2) Already in a correct format.
            3) Requests made for obtaining the view.
        -->
        <condition type="request-uri" operator="notequal">^/(static|app|WEB-INF)/.*</condition>
        <from>^/(.*)</from>
        <to last="true">/app/$1</to>
    </rule>
    <!--
        Removes the /app/ prefix from href of links. NOTE:
        This only works if you use the jstl url tag or spring url tag.
     -->
    <outbound-rule>
        <from>/app/**</from>
        <to>/$1</to>
    </outbound-rule>
</urlrewrite>

Implementing the Web Application

The second step is to implement your web application. You must follow these two principles during the implementation phase:

  1. Add the correct request mappings to controller classes. Remember that you must not add the '/app/' prefix to the value attribute of your @RequestMapping annotation.
  2. Use the url tag of JSTL or Spring tab library in the JSP pages (or simply leave the '/app/' prefix out from the href attribute of the html link tag).

I will use the source code of my example application to provide an example which should clarify the principles given above. The class called HomeController is responsible of processing the requests made to the home page of my example application. The source code of this class is given in following:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

    protected static final String HOME_VIEW = "home";

    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String showPage() {
        return HOME_VIEW;
    }
}

The home view is called home.jsp and its source code is given in following:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html>
<head>
    <title>Spring MVC Restful Url Example Application - Home</title>
    <link rel="stylesheet" type="text/css" href="/static/css/styles.css"/>
</head>
<body>
    <h1>Home</h1>
    <p>
        <!-- 
            When the page is rendered to browser, this links points to '/page'.
            The request made to that url is processed by the PageController class.
        -->
        <c:url value="/app/page" var="pageUrl"/>
        <a href="${pageUrl}">Move to an another page</a>.
    </p>
</body>
</html>

You are Done

I have now described to you how can create RESTful urls with Spring MVC 3.1 And UrlRewriteFilter. This approach feels a bit hacky but I am using it in one of my projects because I have not been able to solve the problem with the default servlet name and Jetty 8.1.0.RC0. Also, if you want to implement RESTful urls by using older Spring MVC versions, this is your best option (It should be fairly easy to create the needed configuration files based on this example).

PS. You can get the soure code of my example application from Github.

10 comments… add one
  • fabou3377 Feb 26, 2012 @ 18:27

    Hi Petri, many thanks for you explanation... But what is the way if you use DISPATCHER_SERVLET_MAPPING = "/"... It isn't works (resources are not loaded) if you go to the url http://localhost:8080/contextpath but if you go to http://localhost:8080/contextpath/, it's works...
    Thanks

  • Hamik Avetisyan Nov 3, 2013 @ 16:28

    Hi!
    Will this work for POST requests? Seems it will not.

    • Petri Nov 3, 2013 @ 16:44

      Hi!

      So, if you use POST requests, the request is not processed by the filter? In other words, if you send a POST request to url '/app/foo', the url is not rewritten to '/foo'?

      I have to confess that I wrote this so long time ago that I don't remember the details anymore. I can investigate this issue though. I will let you know what I find.

      • Petri Nov 3, 2013 @ 18:14

        I tested this with a simple controller method which processes POST requests send to url '/app/hello'.

        The source code of the controller class looks as follows:

        
        import org.springframework.stereotype.Controller;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;
        import org.springframework.web.bind.annotation.ResponseBody;
        
        @Controller
        public class PageController {
        
            @RequestMapping(value = "/hello", method = RequestMethod.POST)
            @ResponseBody
            public String hello() {
                return "World";
            }
        }
        
        

        When I send a POST request to url '/app/hello', the string 'World' is returned in the body of the response.

        Have you compared your application with the example application of this blog post?

  • Christophe M. May 5, 2014 @ 17:52

    Hi,

    Very interesting tutorial, but I've just one simple question: why an URL prefix ("/app" or whatever)? I've already seen this habit, but I never really understood the purpose of it. For now, I always developed without this prefix, and I met no particular issue...

    Thanks!

    • Petri May 6, 2014 @ 18:15

      Hi,

      The URL prefix isn't needed. I used it here because I couldn't figure out a better (and yet simple) example.

  • Vijay Sep 26, 2014 @ 8:09

    Pleaset tell what url should I use to see home or page message.Or what should I use see something happening.As I started server and put url "http://localhost:8080/mvc-restful-urls-with-urlrewritefilter" but it does not work .I also tried other url eg.http://localhost:8080/app/,http://localhost:8080/app/home/,http://localhost:8080/app/page,http://localhost:8080/app/page/.But does not work at all.

    • Petri Sep 26, 2014 @ 9:52

      Hi,

      The idea behind this blog post is that the context path is hidden by the URL rewrite filter. In other words, you can just run the app by running the command mvn jetty:run on the command prompt and browse to the address: localhost:8080.

Leave a Reply