9

I'm trying to migrate a web project off from Jersey to Spring MVC 3.0. The process was really straightforward up to the moment when I started to migrate the controllers supposed to handle URL's with dot notations: "/myApp/resources/create/root.subFolder1". Spring MVC seems to shamelessly cut the ".subFolder1" part from the URL, which happens deep inside framework code (see AbstractUrlHandlerMapping class)

uriTemplateVariables.putAll(getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath));

So my controller method gets invoked with root path parameter, not root.subFolder1

I'd really like to find a way to customize this behavior. Any advices?

PS. The requirement is kinda to keep the existing URL structure, i.e. workarounds like switching to query params "/myApp/resources/create/?path=root.subFolder1" I cannot consider.

PS. My Spring config looks like

<mvc:annotation-driven/>

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="useDefaultSuffixPattern" value="false" />
</bean>

<context:component-scan base-package="my.app.pkg"/>
MrSmith42
  • 9,961
  • 6
  • 38
  • 49
Alexey Buistov
  • 209
  • 2
  • 4
  • 11

6 Answers6

8

Another possibility which may be easier in some circumstances is to add a trailing / to the end of the URL.

So your URL would be /myApp/resources/create/root.subFolder1/

This works in my app using Spring MVC 3.1.

Josh
  • 2,842
  • 8
  • 45
  • 51
8

Try this:

<bean
        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="useDefaultSuffixPattern" value="false" />
    </bean>

This should make it so that Spring won't try to parse extensions.

See also: Spring MVC @PathVariable getting truncated

And: Spring Documentation

Potential config files:

web.xml (where I set the servlet stuff)

<servlet>
    <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/*.xml,classpath*:applicationContext.xml,classpath*:restApplicationContext.xml
        </param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

And then in WEB/INF/spring/mvc-config.xml I have something like this:

<?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:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <bean
        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="useDefaultSuffixPattern" value="false" />
    </bean>
</beans>
Community
  • 1
  • 1
AHungerArtist
  • 9,332
  • 17
  • 73
  • 109
  • If this helped, accept it. If not, further explain the problem :) – AHungerArtist Nov 09 '10 at 17:47
  • Should I declare this bean by itself or out is as a dependency to some other bean? Looks like just declaring it in spring servlet xml config doesn't help... – Alexey Buistov Nov 09 '10 at 17:52
  • I'm assuming the servlet.xml config is where you configure the servlet. Do you use any init-param config for contextConfigLocation? – AHungerArtist Nov 09 '10 at 17:54
  • 1
    This doesn't work for me. The reason is that this property isn't supposed to address my issue, at least the documentation clearly states that "paths which include a ".xxx" suffix or end with "/" already will not be * transformed using the default suffix pattern in any case." – Alexey Buistov Nov 09 '10 at 18:09
  • Addition: when I added DefaultAnnotationHandlerMapping to a spring context, and placed two breakpoints in the code: 1) in setUseDefaultSuffixPattern() and 2) in addUrlsForPath(), both of class DefaultAnnotationHandlerMapping, it appeared that the addUrls gets invoked BEFORE setUseDefaultSuffixPattern, which might be the part of the reason why I can't override spring behavior – Alexey Buistov Nov 09 '10 at 18:29
  • I don't understand why this doesn't work for you. I had the exact same problem you are describing. I feel like though it won't get transformed it's still going to look for a content negotiator of "subFolder1" which it obviously won't find. Maybe you could share the whole of your servlet xml file? – AHungerArtist Nov 09 '10 at 18:41
  • The weird thing I notice from logs is that actual url mapping happens TWICE: [INFO ][.DefaultAnnotationHandlerMapping] - Mapped URL path [/available/{userId}] onto handler 'userConnector' [INFO ][.DefaultAnnotationHandlerMapping] - Mapped URL path [/available/{userId}.*] onto handler 'userConnector' [INFO ][.DefaultAnnotationHandlerMapping] - Mapped URL path [/available/{userId}/] onto handler 'userConnector' [INFO ][.DefaultAnnotationHandlerMapping] - Mapped URL path [/available/{userId}] onto handler 'userConnector' – Alexey Buistov Nov 09 '10 at 18:49
  • Try taking out the mvnc:annotation-driven line and see what happens. – AHungerArtist Nov 09 '10 at 18:49
  • I'm almost certain either mvc:annotation-driven or context:component-scan is overwriting the DefaultAnnotationHandlerMapping that you're trying to add in. – AHungerArtist Nov 09 '10 at 18:54
  • Well, indeed it's the mvc:annotation-driven which causeing the problems. But how to setup annotation support without it? – Alexey Buistov Nov 09 '10 at 19:09
  • Well, here is what it does: http://rapid-web.tumblr.com/post/296916668/what-does-annotation-driven-do Perhaps from that you can determine what you need to use to make things work. I've never used the annotation-driven config because of these types of problems so I usually set things up just for what I need. – AHungerArtist Nov 09 '10 at 19:14
2

In order for

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

to work you have to remove/comment out the

<mvc:annotation-driven />
<mvc:default-servlet-handler />

and any other mvc: configurations defined as they override your custom bean declarations.

Ryba
  • 1,249
  • 10
  • 17
1

This was a bitch. A guy on our team here got it to work over the weekend. We were going to go with underscore but he got it. He removed @Controller from the controller class attributes and left @RequestMapping blank for the class. On the public methods for Http get and put he added "/2.0/" to the @RequestMapping. Example below.

@RequestMapping
public class EndpointController {

@RequestMapping(value="/2.0/endpoint", method = RequestMethod.POST,
consumes=MediaType.APPLICATION_JSON_VALUE, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<EndpointView> create( @Valid @RequestBody EndpointView endpointParams, HttpServletRequest request )

Good luck everyone. This problem sucked.

Robert Beltran
  • 495
  • 3
  • 9
1

I did almost the same as Robert Beltran but I left @Controller annotation for the class. Important is to put "/1.0/" to the @RequestMapping on the methods, not in class annotation.
My example:

@Controller
@RequestMapping("")
public class ClientApi {

private final String API_PREFIX = "/api/1.0/client";

@RequestMapping(value = API_PREFIX + "/{clientId}", method = RequestMethod.GET)
@ResponseBody
public ClientDetailsImpl get(@PathVariable String clientId) {
    // my code
}
}
Michal Korecki
  • 123
  • 1
  • 8
0

The above answer should correct, but be sure to remove <mvc:annotation-driven/> from your XML configuration, otherwise the bean definition is overruled by the one part of the annotation-driven tag.

See the java docs and the source code for all the configuration you miss when this convenience tag is removed.

Michael
  • 629
  • 6
  • 15