Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring invokes wrong controller mapping

I'm building very basic mvc application with Spring. It has one controller that should invoke validation on request body. The problem is that if I define the mapping in web.xml it stops finding the right controller, but when I modify the servlet application context Spring star making some new bindings on the fly but this time annotation based validation is ignored. How can I controll mappings in web.xml while still invoking annotation based validation?

Here are the details:

The controller:

@Controller
@RequestMapping("/api")
public class UserActionsController {

    @RequestMapping(value="/choice", method = RequestMethod.POST)
    public @ResponseBody NameValue addUserChoice(@Valid @RequestBody NameValue action)
    {       
        return action;
    }
}

This is the servlet application context:

<mvc:annotation-driven/>

<context:component-scan base-package="com.my.package" />

<bean
    class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json" />
        </map>
    </property>
    <property name="defaultContentType" value="application/json" />
    <property name="defaultViews">
        <list>
            <bean
                class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
        </list>
    </property>
</bean>

Web xml:

<servlet>
    <servlet-name>action-api</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>action-api</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

The configuration above is working. The problem starts when I try to change web.xml so the controller will only be responsible for "/api/*". I change it to <url-pattern>/api/*</url-pattern>. In that case Spring cannot find the right controller.

(DispatcherServlet:819) - DispatcherServlet with name 'action-api' processing POST request for [/api/choice]
(RequestMappingHandlerMapping:209) - Looking up handler method for path /choice
(RequestMappingHandlerMapping:219) - Did not find handler method for [/choice]
(PageNotFound:1080) - No mapping found for HTTP request with URI [/api/choice] in DispatcherServlet with name 'action-api'
(DispatcherServlet:913) - Successfully completed request

Modifying the servlet application context helps, Spring now able to find the controller, but validation is not invoked anymore.

<bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="alwaysUseFullPath" value="true" />
    <property name="messageConverters">
        <util:list id="beanList">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
        </util:list>
    </property>
</bean>

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

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="alwaysUseFullPath" value="true" />
</bean>

Here is the log:

(DispatcherServlet:819) - DispatcherServlet with name 'action-api' processing POST request for [/api/choice]
(RequestMappingHandlerMapping:209) - Looking up handler method for path /choice
(RequestMappingHandlerMapping:219) - Did not find handler method for [/choice]
(DefaultAnnotationHandlerMapping:124) - Mapping [/api/choice] to HandlerExecutionChain with handler [com.my.package.controller.UserActionsController@1f86dbd] and 1 interceptor
(HandlerMethodInvoker:638) - Reading [com.my.package.model.NameValue] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@2059ef]
(HandlerMethodInvoker:173) - Invoking request handler method: public com.my.package.model.NameValue com.citypath.dima.controller.UserActionsController.addUserChoice(com.my.package.model.NameValue)
(AnnotationMethodHandlerAdapter:1037) - Written [com.my.package.model.NameValue@166685b] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@2059ef]
(DispatcherServlet:957) - Null ModelAndView returned to DispatcherServlet with name 'action-api': assuming HandlerAdapter completed request handling
(DispatcherServlet:913) - Successfully completed request

Looks like Spring making some binding on the fly, but this time validators are ignored. I need to have 2 controllers for, say '/api' and '/something'. How can I define it in web.xml so Spring will be able to locate them and to invoke validation?

Thanks.

like image 695
Dima Avatar asked Aug 08 '12 09:08

Dima


People also ask

How does Spring know which controller calls?

It finds the right controllers by using handler mappings like SimpleUrlHandlerMapping or BeanNameUrlHandlerMapping, which check if the bean name is the same as the view name and the bean implements the View interface.

How do you resolve ambiguous mapping?

If you have created the “/error” url by mistake, change it to another name. This will resolve the exception “Ambiguous mapping.

Does Spring support multiple handler mapping?

@RequestMapping with Request Parameters element, you can have multiple handler methods handling requests to the same URL, but with different parameters.

What is the default controller in spring?

The default handler is based on the @Controller and @RequestMapping annotations, offering a wide range of flexible handling methods. With the introduction of Spring 3.0, the @Controller mechanism also allows you to create RESTful Web sites and applications, through the @PathVariable annotation and other features.


2 Answers

Spring @Controllers URLs are always interpreted relative the the Spring Dispatcher Servlet that handles them. So if you map the dispatcher servlet to /api/* in web.xml then the URL to your controller above is /api/api/choice

So you can configure two spring dispatcher servelts in web.xml one mapped to /api in the web.xml and one mapped to /somethingelse in web.xml then you can just remove /api from the @RequestMappings

In my app I am using a single Dispatcher Servlet for api and UI and I use public static final String called URL in my various API controllers to build up the paths to the various resources exposed by the API. Below is an example from my API.

@Controller
@RequestMapping(CompanyPermissionsResourceController.PATH)
public class CompanyPermissionsResourceController extends BaseApiController
{
    public static final String PATH = CompanyResourceController.PATH + "/permissions"; 
like image 92
ams Avatar answered Sep 18 '22 17:09

ams


Are you sure you are making post request to /api/choice?

@RequestMapping(value="/choice", method = RequestMethod.POST)

Try Changing to

@RequestMapping(value="/choice", method = RequestMethod.GET)
public @ResponseBody NameValue addUserChoice(@Valid @RequestBody NameValue action)
{       
    return action;
}
like image 26
Nandkumar Tekale Avatar answered Sep 19 '22 17:09

Nandkumar Tekale