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.
Carl Sziebert is a loving husband, devoted father, and accomplished software engineer, living and working in the San Francisco Bay Area. He is no stranger to code, having spent the better part of a decade developing software for a diverse range of organizations, including small startups, large corporations, and government agencies. Having built a solid foundation of skills from these experiences, Carl now works as an engineer at 





13 Comments
Thanks for the write-up — but I think the link at the end for the download is broken…
Thanks!
@mariachi:
Thanks for the heads up. Seems that the file went missing after I ran the last wordpress update. It should be fixed now.
hey i trie running your code… it doesn’t work only… i mean the site’s not opening…
@Pranshu:
What are the errors or exceptions you are seeing when you try to run the app?
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?
@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.
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.
Good, clear write up – I like it
Just one side note – don’t include them ‘.svn’ folders in your zip file
rs:
Thanks for the heads up. I’ve purged the .svn files from the downloads.
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
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
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
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
[...] 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 [...]
[...] Exemple d’utilisation d’UrlRewriteFilter avec Spring MVC [...]
[...] UrlRewriteFilter [...]