Just if anybody else cares: I've created a little interceptor to extract ids from the URL in REST style URLs following the pattern /controller/action/id. It's not tested production quality code but maybe somebody else can draw some inspiration from it.
I have a dispatcher servlet context that looks something like this
Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="handlerMappingTemplate" abstract="true">
<property name="interceptors">
<list>
<bean class="some.package.IdFromUrlInterceptor" />
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" parent="handlerMappingTemplate" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" parent="handlerMappingTemplate" />
<context:component-scan base-package="some.package.controllers" />
...
</beans>
and the interceptor looks like this
Code:
package some.package;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.CollectionFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* Interceptor that tries to read an id value from URLs of the pattern /controller/action/id and tries to then set
* the read value on the passed in handler (generally a Controller class) by calling a method with the signature setId(String id) if existent.
*
* @author joerg
*
*/
public class IdFromUrlInterceptor extends HandlerInterceptorAdapter
{
private final Map<String, Method> handlerMethodCache = CollectionFactory.createConcurrentMapIfPossible(16);
private final static Pattern idPattern = Pattern.compile("^/(\\S*)/(\\S*)/(\\S*)");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
Matcher matcher = idPattern.matcher(request.getPathInfo());
if (matcher.matches())
{
try
{
Method idSetter;
if (handlerMethodCache.containsKey(handler.getClass().getName()))
idSetter = handlerMethodCache.get(handler.getClass().getName());
else
{
idSetter = handler.getClass().getMethod("setId", new Class[]{String.class});
handlerMethodCache.put(handler.getClass().getName(), idSetter);
}
if (idSetter != null)
idSetter.invoke(handler, new Object[]{matcher.group(3)});
}
catch (Exception e)
{
//noop
}
}
return true;
}
}
You can then write a controller like this
Code:
@Controller
@Scope("prototype")
public class TestController
{
private String id;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
@RequestMapping("/**/hello/*")
public ModelAndView hello(HttpServletRequest request, HttpServletResponse response) throws Exception
{
System.out.println("id: " + getId());
return new ModelAndView("hello");
}
}
So as long as your controller has a method with the signature void setId(String id) you then should be able to get the extracted id by the time the action is called. Hope this all makes sense.
If somebody knows of a better way to achieve this please let me know.