Results 1 to 6 of 6

Thread: Method level RequestMapping annotations for more REST style actions

  1. #1
    Join Date
    Jul 2006
    Posts
    26

    Default Method level RequestMapping annotations for more REST style actions

    Hi,
    I'm toying with annotation driven controller configuration and I really, really like it.

    Now I just have one issue: I'd like to be able to have more (g)rails like URLs with the pattern /controller/action[/id] but that doesn't seem to be possible as I don't seem to be able to specify a pattern like "action/*" in the method level RequestMapping annotation. If I do, that method never gets mapped. Is there a simple solution to that short of writing my own variant of org.springframework.web.servlet.mvc.annotation.Ann otationMethodHandlerAdapter (which somehow seems like a bad idea) and isn't that something that would be more generally useful?

    Any suggestions would be greatly appreciated.

    Jörg

  2. #2

    Default

    I'm able to do this and it works fine mapping all html requests (below). Have you tried putting two asterisks instead of just one? I also jus tried "/delete/*" and "/delete/**" and they both intercepted a "/delete/person.html?id=1" request.

    Have you tried "/**/action/*"?

    Code:
    @RequestMapping(value="/**/*.html")

  3. #3

    Default

    I actually just tried "/**/action/*" in a webapp I have setup already and seemed like it was working ok. It intercepted '/action/person.html', '/foo/bar/action/person.html', '/foo/bar/action/12', etc.

  4. #4
    Join Date
    Jul 2006
    Posts
    26

    Default

    Thanks David! That's good news - but I actually think now that the controller mapping is the problem. I've always tried with relative paths at the method level RequestMapping and mapping the controller at the type level and it seems that then it doesn't work. Will need to further test that.

    Jörg

  5. #5
    Join Date
    Jul 2006
    Posts
    26

    Default Solved

    OK, it was my fault. The mappings didn't work becuase I didn't explicity add org.springframework.web.servlet.mvc.annotation.Def aultAnnotationHandlerMapping as a bean in my context but inadvertently added a org.springframework.web.servlet.mvc.annotation.Ann otationMethodHandlerAdapter bean instead which kind of made it work half.

    Now if only I wouldn't need to specify '/**/action/*' at th emethod level but 'action/*' I would be in heaven ...

  6. #6
    Join Date
    Jul 2006
    Posts
    26

    Default sample code to extract id from url

    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.
    Last edited by joeslow; Jun 10th, 2008 at 10:09 AM.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •