Spring invokes wrong controller mapping - java

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.

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";

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;
}

I don't think it works like this. Put all the path /api/choice at method level.

Related

Is #responsebody required even when using the MappingJackson2HttpMessageConverter?

I was reading this https://spring.io/guides/gs/rest-service/ and I see that in Spring 4 , the #RestController is a combination of #ResponseBody and #Controller and hence its not necessary to mention the #ResponseBody at every method as it was previously. But, in one of the application am working on, we are using Spring 3.x and we have developed spring webservices and annotated the Controller class as #Controller. The sample controller looks like this:
#Controller
public class SomeController {
#RequestMapping(value = "uri/{values}", method = RequestMethod.GET)
public List<SampleClassPOJO> giveSomething(#PathVariable("values") String some){
//logic
return listOfSampleClassPOJO;
}
//Other services
}
and have the following config in the dispatcher servlet xml :
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prettyPrint" value="false"/>
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper"/>
</property>
</bean>
</list>
</property>
</bean>
The dispatcher servlet is referred in the web.xml
<servlet>
<servlet-name>dispatch</servlet-name>
<servlet-class>someCustom.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatch</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
And we haven't annotated with #ResponseBody for any of the web services method nor at the class level, but still the response is a json. So, when is the #ResponseBody annotation required?
Now, this has gotten me into a confused state, I tried to search for any example with just the above entry in the dispatcher-servlet.xml and without using the #ResponseBody in the methods of the services, but there are no example as such. Only thing I found was, the above entry along with #RestController annotation in Spring 4.
Can anyone please tell me what is the piece of information that am missing here?
Thanks

jax-rs with cxf interceptors and callback handler

I want to transform an existing XML-based webservice to a REST webservice. While the services are working already, I'm struggling with implementing the security.
In the former implementation, we used interceptors like this (file ws-server-context.xml):
<jaxws:endpoint id="someService" implementor="..." address="/..." >
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType" value="PasswordText" />
<entry key="passwordCallbackRef" value-ref="sessionService" />
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
Whenever the address of this endpoint is called, the method handle(Callback[] callbacks) of the bean sessionService is invoked, which checks for proper credentials (username + token). This bean implements the interface CallbackHandler.
How can this approach be implemented in JAX-RS? The endpoints are defined at the webservice classes themself (#Path), so do I need to use any annotations there? How do I register the interceptors?
Thanks for your help!
Instead of the interceptor, you can declare a filter in your web.xml -
<filter>
<display-name>MyFilter</display-name>
<filter-name>MyFilter</filter-name>
<filter-class>com.*.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern></url-pattern> <!-- keep this same as your rest servlet's url pattern -->
</filter-mapping>
This class will be called before your JAX-RS implementation.
You can refer to the callBackHandler from within the filter class.

Spring: servlet-mapping -> url-pattern : /* working but can't display

web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
/WEB-INF/spring/webmvc-config.xml
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml" />
<entry key="html" value="text/html" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
Controller
#Controller
#RequestMapping ( "/" )
public class IndexController extends BaseController
{
#RequestMapping ( "/" )
public String index ( Model model ){
System.out.println("AA");
return index2(model);
}
#RequestMapping ( "/index" )
public String index2 ( Model model ){
System.out.println("BB");
return "index";
}
}
And exist index.jsp File
I guess that is very good working
BBBBBBBBBBBUUUUUUUUUTTTTTTTTT, BUT!
WHY????
WHY????
WHY????
WHY????
And More strange
??????????????????????????????????????????????????????????????????
Controller work it!! but don't display browser
What's going on?
Please help me.
And Log
DispatcherServlet with name 'dispatcher' processing GET request for [/WEB-INF/views/index.jsp]
No mapping found for HTTP request with URI [/WEB-INF/views/index.jsp] in DispatcherServlet with name 'dispatcher'
Servlet containers have rules for how they map and handle URI requests. These can be found in the Servlet Specification. It's also important to note that most Servlet containers have a Servlet to handle JSPs, mapped to *.jsp, which is an extension mapping. Tomcat has a JspServlet to do this.
You've mapped your DispatcherServlet to
<url-pattern>/*</url-pattern>
which is a path mapping. Path mappings take precedence over extension mappings. So when you submit your view name
return "index";
Spring will use the ViewResolver
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
to resolve a path to use with a RequestDispatcher's forward method. That path will be /WEB-INF/views/index.jsp. Now the Servlet container will receive that path and attempt to find a Servlet to handle it. Since you have a Servlet mapped to /* it will use it, but your DispatcherServlet doesn't have a mapping for that path and therefore responds with a 404.
The simple solution is to change your mapping to /, which is the default handler if no other matches are found. In this case, when you submit your view and the container must find a mapped Servlet, it will find the JspServlet and use it.

spring mvc #pathvariable not working with url file extension

i am facing an issue while invoking spring mvc method which has pathvariable
#RequestMapping(value="/retrieveData/{userId}", method=RequestMethod.POST)
public #ResponseBody
ModelAndView retrieveData(#PathVariable Long userId , HttpSession session) {
i have web.xml servlet mapping as below
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
in my jsp , i have my javascript
document.getElementById("form1").action = "retrieveData.html/2";
document.getElementById("form1").submit();
so my question is, will path variable works with url file extension in my case i have *.html , which i eventually want my servlet to map /retrieveData/{userId}
while submitting the page i am getting HTTP Status 400 error. help needed
EDIT:- i have ContentNegotiationManagerFactoryBean configure in my spring context
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="false" />
<property name="favorParameter" value="true" />
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>

bind Spring HandlerInterceptor only to one controller

Using Spring 3.0.2.RELEASE. I'm having 2 Controllers in package com.myCompany. The Controllers are activated via Component-scan
<context:component-scan base-package="com.myCompany" />
then I'm having a interceptor bind to the 2 controllers via
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptor"/>
</list>
</property>
</bean>
How can i bind the interceptor to only one specific Controller or to only certain methods inside a Controller?
Background: I want to inspect the URL that it contains certain parameters
Docu Link
When you inject interceptors into a HandlerMapping bean, those interceptors apply to every handler mapped by that HandlerMapping. That was fine in the pre-annotation days, since you'd just have configure multiple HandlerMapping beans. However, with annotations, we tend to have a single DefaultAnnotationHandlerMapping that maps everything, so this model doesn't work.
The solution is to use <mvc:interceptors>, where you explicitly map paths to interceptor beans. See the docs, and this example:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/secure/*"/>
<bean class="org.example.SecurityInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

Categories