5

For some reason, my @PathVariable annotation is not working at all, after doing some Google search I've not been able to find anyone else with the same issue, this is the code:

@Controller
@RequestMapping("/bot")
public class BotController {
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public void test() {
        System.out.println("test");
        Store.INSTANCE.getChatBot().postMessage("test");
    }

    @RequestMapping(value = "/say/{text}", method = RequestMethod.GET)
    public void say(final @PathVariable("text") String text) {
        System.out.println("say: " + text);
        Store.INSTANCE.getChatBot().postMessage(text);
    }
}

This works: http://localhost:8080/GithubHookSEChatService/bot/test

This does not work: http://localhost:8080/GithubHookSEChatService/bot/say/realtest

Apart from that the System.out.println("say: " + text) does not happen, the only other clue I have is:

24-Aug-2014 17:25:21.611 WARNING [http-apr-8080-exec-24] 
org.springframework.web.servlet.PageNotFound.noHandlerFound 
No mapping found for HTTP request with URI 
[/GithubHookSEChatService/bot/say/realtest] in DispatcherServlet with name 'dispatcher'

I'm running out of clues, does anyone know what is happening? Why doesn't the latter work?

My relevant web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

and

dispatcher-servlet.xml:

<?xml version='1.0' encoding='UTF-8' ?>
<!-- was: <?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:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" xmlns:context="http://www.springframework.org/schema/context">

    <!-- auto scan -->
    <context:component-scan base-package="com.skiwi.githubhooksechatservice" />

    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <value>classpath:githubhooksechatservice-environment.properties</value>
        </property>
    </bean>

    <!-- properties -->
    <bean id="configuration" class="com.skiwi.githubhooksechatservice.mvc.configuration.Configuration">
        <property name="rootUrl" value="${env.rootUrl}"/>
        <property name="chatUrl" value="${env.chatUrl}"/>
        <property name="botEmail" value="${env.botEmail}"/>
        <property name="botPassword" value="${env.botPassword}"/>
        <property name="roomId" value="${env.roomId}"/>
    </bean>

    <!-- startup bean -->
    <bean name="startup" init-method="start" class="com.skiwi.githubhooksechatservice.mvc.beans.StartupBean" lazy-init="false" />
</beans>
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • Have you enabled the logging for this module and studied both the initialization, where each mapping is reported, and request processing, where it should be reported that no matching controller method was found? I recently used this functionality and didn't experience problems. – Marko Topolnik Aug 24 '14 at 15:22
  • @MarkoTopolnik It gives me the following: `24-Aug-2014 17:25:21.611 WARNING [http-apr-8080-exec-24] org.springframework.web.servlet.PageNotFound.noHandlerFound No mapping found for HTTP request with URI [/GithubHookSEChatService/bot/say/realtest] in DispatcherServlet with name 'dispatcher'`. – skiwi Aug 24 '14 at 15:25
  • And you saw the line which says "mapping URL ... to controller method ..." or similar? – Marko Topolnik Aug 24 '14 at 15:31
  • Yes @MarkoTopolnik `24-Aug-2014 17:31:20.333 INFO [http-apr-8080-exec-40] org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping.registerHandler Mapped URL path [/bot/*] onto handler 'botController'`, as `http://localhost:8080/GithubHookSEChatService/bot/test` does work. I'm not able to see the exact mappings of both methods in the log though. – skiwi Aug 24 '14 at 15:33
  • Hm... one thing I would add is `@ResponseBody` in front of `void` return type. – Marko Topolnik Aug 24 '14 at 15:33
  • That output is suspicious to me... I always see specific mapping for each method. It may indeed indicate that nothing was actually mapped. Please try my above advice. – Marko Topolnik Aug 24 '14 at 15:33
  • The `@ResponseBody` is a lot more helpful for seeing whether a request succeeded :) On `test` I have a blank page, on `say` a 404. – skiwi Aug 24 '14 at 15:34
  • I would rarely name the path variable in Spring. I simply say `@PathVariable` instead of `@PathVariable("name")`. Also, you may want to double check that your import is that of `org.springframework.web.bind.annotation.PathVariable` it wouldn't be a rare case that you actually imported a `@PathVariable` from a different package, like Jersey or any other library you have in your project. Finally, I doubt that you need a `@ResponseBody` since your method returns nothing. – Edwin Dalorzo Aug 24 '14 at 15:36
  • 1
    I have an useful lead now: `@RequestMapping(value = "/{text}", method = RequestMethod.GET)` does work with `http://localhost:8080/GithubHookSEChatService/bot/realtest`, but `@RequestMapping(value = "/say/{text}", method = RequestMethod.GET)` still does not work with `http://localhost:8080/GithubHookSEChatService/bot/say/realtest`. – skiwi Aug 24 '14 at 15:38
  • @EdwinDalorzo that used to work only if the project was compiled with debug info enabled. I'm not sure if that still is the case. – soulcheck Aug 24 '14 at 15:45
  • @skiwi can you put your web.xml? – Federico Piazza Aug 24 '14 at 15:45
  • @skiwi have you checked this post http://stackoverflow.com/questions/8286874/spring-mvc-pathvariable-and-requestparam-blank-empty-in-jsp-view? – Federico Piazza Aug 24 '14 at 15:46
  • Your methods seems to work correctly, did `RequestMappingHandlerMapping` logged your mapping in project startup? – Hamid Samani Aug 24 '14 at 15:46
  • I'd try cleaning and rebuilding the project. Looks like you might be using some old version of the class. – soulcheck Aug 24 '14 at 15:50
  • Is anyone able to get this mapping working locally? I am running Java 8 with the newest Spring MVC and newest Tomcat, so that might matter if I'm really out of luck. – skiwi Aug 24 '14 at 16:04
  • Try removing the working mapping ans see what happens. – soulcheck Aug 24 '14 at 16:21
  • @soulcheck That didn't change anything either. – skiwi Aug 24 '14 at 16:44
  • The problem does not seem to have anything to do with the `@PathVariable` anymore, even `@RequestMapping(value = "/test/test", method = RequestMethod.GET)` on the `test()` method does not work. – skiwi Aug 24 '14 at 16:46
  • That's why i suspect some issue with refreshing classes etc. Does the first mapping work if you remove it from the code? Cause that would confirm my suspicion. – soulcheck Aug 24 '14 at 16:49
  • @soulcheck We might've misunderstood. If both `test()` and `say()` are present, then `test()` works and `say()` fails. If only `say()` is present, then `test()` does not work and `say()` still fails. – skiwi Aug 24 '14 at 16:50
  • Ok, cool so classpath sync problems are out of the question. – soulcheck Aug 24 '14 at 16:54
  • possible duplicate of [In Spring MVC, how can I map nested URLs such as /settings/, /settings/users/, and /settings/users/delete?](http://stackoverflow.com/questions/6513112/in-spring-mvc-how-can-i-map-nested-urls-such-as-settings-settings-users-a) – Peter O. Aug 24 '14 at 22:25

2 Answers2

3

This issue appears to not be related to @PathVariable only, it actually also fails on:

@RequestMapping(value = "/test/test", method = RequestMethod.GET)
@ResponseBody
public void test() {
    System.out.println("test");
    Store.INSTANCE.getChatBot().postMessage("test");
}

This happens because there are multiple levels of subpaths, I found the solution of my issue in In Spring MVC, how can I map nested URLs such as /settings/, /settings/users/, and /settings/users/delete?

The only thing you need to do, is to include the following in your dispatcher-servlet.xml:

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

And then everything works as expected!

Community
  • 1
  • 1
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • Have you considered going XML-less? It is a relief to be able to work with Java only, including the benefits of type safety and automatic imports. – Marko Topolnik Aug 24 '14 at 17:40
  • @MarkoTopolnik I've considered it, but didn't manage to get it working so far with for example the configuration file. – skiwi Aug 24 '14 at 17:41
  • You mean the .properties file? – Marko Topolnik Aug 24 '14 at 17:45
  • still, that AnnotationHandler is used by default so you have to have some non-standard config somewhere. My guess is that you simply miss `` in your context. That other answer is wrong in that it isn't `component-scan` that adds `DefaultAnnotationHandlerMapping` to the context, as it's pretty much oblivious to the mvc-specific things. – soulcheck Aug 24 '14 at 19:50
0

I know this is old question. But the answer will be helpful for others.

(1) Add below line to dispatcher-servlet.xml assuming dispatcher is your DispatcherServlet name in web.xml

<mvc:annotation-driven/>

Raghunath
  • 1
  • 1