Parameterized REST URLs with Spring MVC

Posted on by in Development

At Carbon Five, we’ve been working REST-ful practices into our web applications for some time now. Providing simple URLs for application entities is a key principal of this style, but parsing parameters out of the request path has been klunky in Spring MVC. Spring’s WebFlow apparently supports REST-ful URLs, but I’ve never found anything in what I’ve read or heard to recommended that project (though I’ve heard nothing bad).

I finally got fed up with the situation and worked out a solution that lets developers specify path parameters in the dispatcher mappings, which will appear as request parameters in the controller and view. I’ve made the project available on our public SVN server. The solution requires only 4 classes, so you can download them instead. Read on for configuration information.

ParameterizedUrlHandlerMapping

The solution is to replace Spring MVC’s SimpleUrlHandlerMapping with ParameterizedUrlHandlerMapping. This class is responsible for routing requests to the appropriate handler (servlet, controller, or JSP), so it’s an ideal place to specify the parameters. Of course, at this point, it has no more access to the request than a controller, so the best it can do is add the path parameters to the request attributes. It adds them as a Map of String name/value pairs with the key “ParameterizedUrlHandlerMapping.path-parameters”.

To use ParameterizedUrlHandlerMapping, replace the SimpleUrlHandlerMapping bean in the dispatcher configuration file
with something that looks like this:

<bean class="carbonfive.spring.web.pathparameter.ParameterizedUrlHandlerMapping">
  <property name="alwaysUseFullPath" value="true"/>
  <property name="mappings">
    <props>
      <prop key="/view/noparameters">controller1</prop>
      <prop key="/view/(bar:foo)">controller2</prop>
      <prop key="/view/(*.html:html)">controller3</prop>
      <prop key="/view/(**/*:view).view">controller4</prop>
      <prop key="/view/c/(*:controller)/(*:id)">controller5</prop>
    </props>
  </property>
</bean>

The ParameterizedUrlHandlerMapping supports all mappings that are valid using SimpleUrlHandlerMapping’s default AntPathMatcher. The special parenthetical sections of the patterns have the syntax:

'(' + [ant_style_path] + ':' + [parameter_name] + ')'

Any part of the path pattern can be within parenthesis. The above example will having the following effect:

.content table {
width: 100%;
border: 1px solid #ccc;
}
.content th {
background-color:#eee;
}
.content td {
background-color: #f5f5f5;
}

path controller parameters

/view/noparameters controller1
/view/bar controller2 foo -> bar
/view/piglet.html controller3 html -> piglet.html
/view/this/that/the-other.view controller4 view -> this/that/the-other
/view/c/save/2342443 controller5 controller -> save
id -> 2342443

ParameterizedPathFilter

If accessing the parameters from request attributes is all you need, you can stop here. But to elevate your path parameters to first class citizen status in your web app, you need them to appear as request parameters. Once they are request parameters, Spring will bind them to your command or form objects, and your controllers can once again forget the details of request parsing and focus on business logic. To complete the solution, we need the ParameterizedPathFilter configured as a web filter. It proxies the request with a wrapper class that listens for ParameterizedUrlHandlerMapping’s request attribute. When it is set, the wrapper class adds the parameters to the request parameters for the remaining life of the request.

To configure the ParameterizedPathFitler, add the following to your web.xml:

<filter>
  <filter-name>PathParameterFilter</filter-name>
  <filter-class>carbonfive.spring.web.pathparameter.ParameterizedPathFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>PathParameterFilter</filter-name>
  <url-pattern>/path/to/dispatcher/servlet/*</url-pattern>
</filter-mapping>

You should have a filter mapping for each path pattern mapped to a Spring dispatcher servlet.

That’s all it takes to add this functionality to your webapp. We’ve been using it in our current project. It’s been problem free and the architectural improvements have been satisfying.