RESTful URLs with Spring MVC and UrlRewriteFilter

Filed under Java, Spring Framework, Tutorials

While Spring 3.0 promises to support REST style URLs out of the box, it won’t ship until sometime later this year. Spring 3.0 M1 will offer this functionality for anyone brave enough to work with potentially unstable technologies and should be available soon. And while this is good news for those starting out on new projects (or those willing to undergo a significant refactoring), most won’t want to migrate their applications just for RESTful URLs. All hope is not lost though, thanks to Paul Tuckey’s UrlRewriteFilter.

With this short tutorial, I’ll demonstrate how easy it is to configure UrlRewriteFilter for use within your Spring MVC application. I should mention that this technique will work well for just about any Java-based web framework, like JSF or Struts.

For those of you that want to skip to the end, I’ve created a small, functional sample application which demonstrates the techniques described in this tutorial. It can be downloaded here.

Getting started, we need to register the filter within our application’s web.xml file.

web.xml

    <!-- UrlRewriteFilter -->
    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>
            org.tuckey.web.filters.urlrewrite.UrlRewriteFilter
        </filter-class>
        <init-param>
            <param-name>logLevel</param-name>
            <param-value>WARN</param-value>
        </init-param>
    </filter>

    <!-- UrlRewriteFilter Mapping -->
    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

That out of the way, we can dig into the application itself. Let’s review an example controller class as it might exist in your application today.

SprocketsController.java

@Controller
public class SprocketsController {

    private final SprocketService service;

    @Autowired
    public SprocketsController(SprocketService service) {
        this.service = service;
    }

    @RequestMapping("/sprockets/list.do")
    public String list(ModelMap model) {
        model.addAttribute("sprockets", service.list());
        return "sprocket/list";
    }

    @RequestMapping("/sprocket/display.do")
    public String display(@RequestParam("sprocketId") int sprocketId, ModelMap model) {
        model.addAttribute("sprocket", service.find(sprocketId));
        return "sprocket/display";
    }

    @RequestMapping("/sprocket/edit.do")
    public String edit(@RequestParam("sprocketId") int sprocketId, ModelMap model) {
        model.addAttribute("sprocket", service.find(sprocketId));
        return "sprocket/edit";
    }
}

Our controller contains actions for displaying a list of sprockets, drilling down into each sprocket’s details and editing an individual sprocket. Each action is mapped to a URI path via the @RequestMapping annotation and, as you can see, all three defined here handle requests mapped to the *.do extension. The beauty of the solution I’m demonstrating here is that you should not need to make any changes to your application code at all. That in mind, let’s move on to the interesting part, configuring our application to respond to a REST style URL like http://localhost:8080/sprocket/1234/edit instead of what we’ve got now: http://localhost:8080/sprocket/edit.do?sprocketId=1234. You’ll need to create a new configuration file named urlrewrite.xml and make it available on the classpath. (For those with a distaste for XML based configuration, the latest version of the filter offers a means to generate the configuration via annotations. Details on that can be found here.) We’ll start our configuration by defining a few rules to handle the incoming requests.

urlrewrite.xml

    <rule>
        <from>^/sprockets/$</from>
        <to>/sprockets/list.do</to>
    </rule>
    <rule>
        <from>^/sprocket/([a-z0-9]+)/$</from>
        <to>/sprocket/display.do?sprocketId=$1</to>
    </rule>
    <rule>
        <from>^/sprocket/([a-z0-9]+)/edit$</from>
        <to>/sprocket/edit.do?sprocketId=$1</to>
    </rule>

Let’s take a look at what each one of these rules does. The first rule states that the UrlRewriteFilter should transparently forward each request for http://localhost:8080/sprockets/ to the existing application URL of http://localhost:8080/sprockets/list.do. The second and third rules handle the display and edit requests. These 2 rules define simple regular expressions that are parsed out and appended to the destination URI as query string parameters. If you have more than 1 query string parameter, you’ll need to use & XML entity. (It should be noted that you can use wildcard matching (*) instead, however it lacks some of the flexibility offered by regular expressions.)

That takes care of the inbound URLs, but we still have a problem. Within our application, we have a number of links which point to the old URL structure. No worries, UrlRewriteFilter tackles this issue with ease. By examining the response, the filter can rewrite the existing links defined within anchor tags, i.e. <a href="<c:url value='/sprockets/list.do'/>">Return to the list.</a>. Let’s take a look at the rule definitions to handle this.

urlrewrite.xml

    <outbound-rule>
        <from>^/sprockets/list.do$</from>
        <to>/sprockets/</to>
    </outbound-rule>
    <outbound-rule>
        <from>^/sprocket/display.do\?sprocketId=([a-z0-9]+)$</from>
        <to>/sprocket/$1/</to>
    </outbound-rule>
    <outbound-rule>
        <from>^/sprocket/edit.do\?sprocketId=([a-z0-9]+)$</from>
        <to>/sprocket/$1/edit</to>
    </outbound-rule>

More or less, these outbound rules are just the inverse of the inbound rule definitions. All it takes is a simple regular expression to parse out the sprocketId. One common gotcha to note here is that you need to add the \ character before the start of the query string marked by the question mark. If you don’t do this, the filter won’t be able to process your links.

Pretty easy, right? With your rules in place, fire up your application and give it a try. You can always examine the status of the filter by visiting http://127.0.0.1:8080/rewrite-status. If you’re a bit hesitant to try this out on your own app, fret not, I’ve created a simple, yet functional sample application which you can use to experiment with.

The example source code can be downloaded here.

Liked this post? Share it with others:
  • Digg
  • del.icio.us
  • Facebook
  • DZone
  • email
  • LinkedIn
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

13 Comments

  1. mariachi
    Posted September 27, 2008 at 9:11 pm | Permalink

    Thanks for the write-up — but I think the link at the end for the download is broken…
    Thanks!

  2. Posted September 28, 2008 at 7:40 am | Permalink

    @mariachi:

    Thanks for the heads up. Seems that the file went missing after I ran the last wordpress update. It should be fixed now.

  3. Posted January 11, 2009 at 1:19 pm | Permalink

    hey i trie running your code… it doesn’t work only… i mean the site’s not opening…

  4. Posted January 11, 2009 at 9:10 pm | Permalink

    @Pranshu:

    What are the errors or exceptions you are seeing when you try to run the app?

  5. mike
    Posted February 13, 2009 at 9:30 am | Permalink

    I like the example, but I cannot get the code to work either. Does the code need to be recompiled or can I just drop the ‘web’ directory into Tomcat?

  6. Posted February 19, 2009 at 9:42 am | Permalink

    @mike:

    What are the errors you are seeing when you try to run the code? I compiled it using JDK6 and ran it in Tomcat 6 on my MacBook Pro.

  7. Posted February 20, 2009 at 10:55 am | Permalink

    I’ve updated the sample application to use Ivy to manage the dependencies for the project. Going forward, you’ll need to compile and deploy the application yourselves. This should remedy the issues people were having when running it. As an added benefit, the sample application download is significantly smaller now that the JARs are retrieved automagically by Ivy.

  8. Posted April 29, 2009 at 1:40 am | Permalink

    Good, clear write up – I like it

    Just one side note – don’t include them ‘.svn’ folders in your zip file :)

  9. Posted April 29, 2009 at 8:41 am | Permalink

    rs:

    Thanks for the heads up. I’ve purged the .svn files from the downloads.

  10. Tjerk
    Posted June 9, 2009 at 6:54 am | Permalink

    Carl,

    I like your solution, since it’s nicely layered.
    Small point of improvement however:


    REST style URL like
    http://localhost:8080/sprocket/1234/edit

    Should be
    http://localhost:8080/sprocket/1234/

    Since actions like ‘edit’ should not be included in the URL but should be purely declared via HTTP methods in the request header like ‘GET/PUT/POST/DELETE’. The ‘edit’ action on your controller should then be targeted by using the PUT method in the header of the request. The @RequestMapping annotation let’s you do this.
    This way you connect the action based Spring MVC way with the resource based REST way.

    Spring 3 has better support for this, but since you mention, it won’t be available for some time.

    Kind regards,
    Tjerk

  11. Posted August 27, 2009 at 7:08 am | Permalink

    Hey Carl, I am trying to get the urlrewrite filter to work with 1 parameter… I am getting it to work with simple urls such as

    The rule means that requests to /test/status/ will be redirected to /rewrite-status
    the url will be rewritten.

    orderportal/custom/CUSTOM_PAGE_5
    orderportal/custom/CUSTOM_PAGE_3

    but having problems when trying to filter with a parameter in the url like:

    The rule means that requests to /test/status/ will be redirected to /rewrite-status
    the url will be rewritten.

    ^/orderportal/home?switchprofile=$mike
    orderportal/custom/CUSTOM_PAGE_4

    I have tried lots of variations on the parameter like ?switchprofile={encode:mike}, ?switchprofile=mike$, ?switchprofile=$mike. No luck.

    Any direction would be most helpful. Posted on UrlRewrite google group as well.

    Btw, ironic I landed on your site.

    Mike

  12. Posted August 28, 2009 at 7:25 am | Permalink

    Well, got it working the way I needed. I was messing up on my in that I wasnt writing correct regular expressions. Here is an example of my working code with 1 parameter

    orderportal/home\?switchprofile\=mike$
    op_urlrewrite/orderportal/custom/CUSTOM_PAGE_3?switchprofile=steph

    Thanks

  13. Thas
    Posted December 2, 2009 at 9:56 pm | Permalink

    HI, I am using struts2 with spring & tiles

    I have the following rule

    ^/test5\.html$
    /welcome.action

    but getting HTTP Status 404

    however if I change the rule to

    ^/test5\.html$
    /star_test.jsp

    it works…
    can you please tell me what I am missing here

    thanks alot

3 Trackbacks

  1. By Recent Links Tagged With "urls" - JabberTags on December 5, 2008 at 9:05 am

    [...] URLs for a better library website – walking paper Saved by wilkojunior86 on Wed 03-12-2008 RESTful URLs with Spring MVC and UrlRewriteFilter Saved by Thaihotweb on Fri 14-11-2008 Breaking the Blogfast Saved by ianianian67 on Mon [...]

  2. [...] Exemple d’utilisation d’UrlRewriteFilter avec Spring MVC [...]

  3. [...] UrlRewriteFilter [...]