I followed the documentation for HandlerInterceptors. Noting that in the new version of Spring: "the configured interceptor will apply to all requests handled with annotated controller methods".
The following is in an xml configuration file:
I have an annotated controller beginning like this:
When I request a url that executes the controller's code, my interceptor code is never called. Can anyone please explain why?
The interceptor code is:
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class DomainNameInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
System.out.println("Why is this not called?");
return true;
}
}
I was using the following documentation:
Spring Core 3.1.x Documentation
I did a search for HandlerInterceptor and followed the example given within the documentation in the included link.
If you have configured your MVC context using <mvc:annotation-driven/>,then I think the handlerMapping created when defining beans based on this custom namespace is overriding the handlerMapping that you have defined. A better way to register your interceptors would be to use the <mvc:interceptors> subtag to define the interceptors, this way it will get registered to the correct handlerMapping:
<mvc:annotation-driven>
<mvc:interceptors>
<ref bean="interceptor"/>
</mvc:interceptors>
</mvc:annotation-driven>
Biju's answer above is correct except in spring 3.1 you have to do this:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/pathToIntercept/**" />
<bean class="com.foo.bar.Interceptor" />
</mvc:interceptor>
</mvc:interceptors>
This question might be old, but in case someone bumps into it looking for answers like I did, the use of a MappedInterceptor as described in the answer below worked for me.
https://stackoverflow.com/a/35948730/1705048
Related
I have a configuration class, which extends WebMvcConfigurationSupport and I have added interceptors like this:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor()).addPathPatterns("/api/**");
}
where myInterceptor() is:
#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}
and it works fine for any mapping (/api/**) which I have implemented for example /api/sample - preHandle from MyInterceptor is fired (I have a Controller with mapping /api/sample).
When I call not existing resource for example /api/forward/sample preHandle from MyInterceptor is never called.
Please notice it worked as expected when I had the config in xml, like:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**" />
<bean class="my.pack.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
For some reason with java configuration requests for not existing mappings are not intercepted. Why the configuration is not equivalent? I thought it should be.
EDIT:
A bit more debugging information. With xml configuration DispatcherServlet#handlerMappings contains 5 handlers:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.socket.server.support.WebSocketHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
with Java configuration it contains 7 handlers:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.socket.server.support.WebSocketHandlerMapping
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
org.springframework.security.oauth2.provider.endpoint.FrameworkEndpointHandlerMapping
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
The problem seems to be with SimpleUrlHandlerMapping (at least it seems to be used for the resource I call - api/forward/sample while for api/sample RequestMappingHandlerMapping is in use) which has empty adoptedIterceptors in the case with Java based configuration.
EDIT 2:
Full source code for sample application (I tried to make it as small as possible just to demonstrate the effect):
https://github.com/szprutamich/spring-demo
In class ConfigurationBase - configuration can be switched from xml based to java based with static field CONFIG.
With xml based config both urls work:
/api/sample
/api/forward/sample
With java based config forward does not work.
Your question is about a "not existing request mapping", but in your XML configuration, it exists :
<default-servlet-handler xmlns="http://www.springframework.org/schema/mvc" />
This declares a default handler for all requests, and a request interceptor ony works if a valid handler is found. Remove this line and you will get the same behavior in both XML and Java configs : no default handler will be found and the interceptor won't work.
So for the interceptor to work on all requests in your Java configuration, you have to declare a default handler. You could override configureDefaultServletHandling but afaik it's not possible to configure interceptors on it. You may have to explicitly map /** in a default handling controller.
I'm using the latest Spring (4.1.1) with tomcat 7.0.52 . I have enabled annotation driven mvc.
In my application, I defined an interceptor:
<mvc:interceptors>
<bean class="com.abc.UserAgentChangeInterceptor"/>
</mvc:interceptors>
Alongside, I defined resources:
<mvc:resources mapping="/static/**/*" location="/desktop"/>
<mvc:resources mapping="/**/*.css" location="/"/>
<mvc:resources mapping="/**/*.js" location="/"/>
<mvc:resources mapping="/**/*.gif" location="/"/>
<mvc:resources mapping="/**/*.htm" location="/"/>
<mvc:resources mapping="/**/*.svg" location="/"/>
<mvc:resources mapping="/**/*.png" location="/"/>
However, when request come in, even for static resources/assets, they still get routed to interceptors first.
I am aware of the following practice that can solve the problem:
use mvc:exclude-mapping inside mvc:interceptor to exclude the requests made on the above url being routed to my interceptor, but it seems to violate the DRY principle, and I really don't like the look and feel.
use mvc:mapping on interceptors. However, I am using annotation driven #RequestMapping on so many handlers and there are so many different path. That solution also doesn't work cleanly for me.
I also came across opinions saying using #ControllerAdvice instead of interceptors can help alleviate the issue, but based on what I've learned by reading Spring documentation, #ControllerAdvice doesn't have any method similar to preHandle() that executes before #Controllers
I still prefer to solve this problem using the Spring XML configuration approach rather than defining a #Configuration class in my application. Just a convention thing.
Any opinion is deeply appreciated!
Approach-1: Use mapping path in interceptor configuration
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/path" />
<bean
class="bean" />
</mvc:interceptor>
</mvc:interceptors>
Approach-2: along with mvc:resources, specify
<mvc:annotation-driven/>
The current "solution" that I found is to use aspect. For example, if I want to intercept only request to #RequestMapping method in #Controller. I would do
#Aspect
#Order(1)
#Component
public class UserAgentChangeAspect {
#Pointcut("execution(public * *(..)) && #within(org.springframework.stereotype.Controller) && #annotation(org.springframework.web.bind.annotation.RequestMapping)")
private void pointcut() {
}
#Before("pointcut()")
public void preHandle()
throws Exception {
// logic
}
}
#After("pointcut()")
public void postHandle()
throws Exception {
// logic
}
}
I will mark this one as Accepted. If anyone has better idea please post it and I'll accept it if I see fit.
My existing Spring Web MVC application has the following handler mapping in the Controller.
#RequestMapping(method = RequestMethod.GET, value = "/welcome")
I trigger the following requesthttp://www.example.com/welcomeand this works fine.
The problem is
http://www.example.com/welcome.check.blah
also works!!!
Also, a HTTP GET request URL to the application with script tag is getting redisplayed though it fails the authorization.
Example http://www.example.com/welcome<script>alert("hi")</script> gets redisplayed as such in the browser window and as a result of my authorization logic "Not authorized" message is displayed.
I wonder if this is a security issue and should I need do any encoding/filtering in the code?
This behavior is due to the option useSuffixPatternMatch which is true by default inside the RequestMappingHandlerMapping (I assume you use Spring MVC 3.1).
useSuffixPatternMatch :
Whether to use suffix pattern match (".*") when matching patterns to requests. If enabled a method mapped to "/users" also matches to "/users.*". The default value is "true".
To set useSuffixPatternMatch to false, the easiest way is to use #Configuration :
#Configuration
#EnableWebMvc
public class Api extends WebMvcConfigurationSupport {
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping();
mapping.setUseSuffixPatternMatch(false);
return mapping;
}
}
In current Spring Java config, there is a slightly easier way to configure the same thing:
#Configuration
public class DispatcherConfig extends WebMvcConfigurationSupport {
#Override
protected void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}
When you use Spring to request a mapping of that type (i.e. "/anything") Spring actually maps your controller to several URLs:
/welcome
/welcome.*
/welcome/
To prevent this - either be more specific when you RequestMapping (i.e. /welcome.htm ), or manually map the URL to controller in your Xml config:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome">YourControllerBean</prop>
</props>
</property>
</bean>
Cheers, Pete
You can also restrict this in the web.xml by mentioning the url pattern. Instead of giving "/", you can mention "/.htm" in your web.xml.
Something like
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/application/*.htm</url-pattern>
</servlet-mapping>
You can use the useDefaultSuffixPattern property.
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
Also refer URL Pattern Restricting in SPRING MVC
Starting from Spring framework 5.3 useDefaultSuffixPattern is deprecated and turned off by default. Spring upgrade notes, section "Use of Path Extensions Deprecated in Spring MVC"
In an app I'm looking at I find this in the dispatcher xml:
<mvc:annotation-driven />
Am I correct that this is the Spring 3 way of defining handler mappings (url routes). One of the controllers in the app looks like this:
#Controller
#RequestMapping("/order")
#SessionAttributes(OrderController.ORDER)
public class OrderController
{
//...
I assume that the line
#RequestMapping("/order")
is the actual definition of the handler mapping for this url route.
Am I correct that the older way of defining this handler mapping would have been with one of:
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping
ControllerClassNameHandlerMapping
CommonsPathMapHandlerMapping
Yes. <mvc:annotation-driven /> is a convenience option for configuring Annotation-driven controllers. It configures special HandlerMappings and HandlerAdapters.
See the section in the Spring reference manual about <mvc:annotation-driven/> for a full list of what specifying this actually does.
As an alternative, you could always specify the DefaultAnnotationHandlerMapping, AnnotationMethodHandlerAdapter, etc. beans yourself manually.
It should be easy:
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptor" />
</list>
</property>
</bean>
but this way the interceptor isn't called.
By default, Spring will register a BeanNameUrlHandlerMapping, and a DefaultAnnotationHandlerMapping, without any explicit config required.
If you define your own HandlerMapping beans, then the default ones will not be registered, and you'll just get the explicitly declared ones.
So far, so good.
The problem comes when you add <mvc:annotation-driven/> to the mix. This also declares its own DefaultAnnotationHandlerMapping, which replaces the defaults. However, if you also declare your own one, then you end up with two. Since they are consulted in order of declaration, this usually means the one registered by <mvc:annotation-driven/> gets called first, and your own one gets ignored.
It would be better if the DefaultAnnotationHandlerMapping registered by <mvc:annotation-driven/> acted like the default one, i.e. if explicitly declared ones took precedence, but that's not the way they wrote it.
My current preference is to not use <mvc:annotation-driven/> at all, it's too confusing, and too unpredictable when mixed with other config options. It doesn't really do anything especially complex, it's not difficult or verbose to explicitly add the stuff that it does for you, and the end result is easier to follow.
Problem I faced: Spring MVC tag doesn't go well with custom definition of DefaultAnnotationHandlerMapping.
Why..? the reason is very well explained in the answers above.
Why i wanted to use DefaultAnnotationHandlerMapping? I want to define an interceptor for my every request. a Spring-Mobile interceptor to determine the USER AGENT..mobile or a browser?
Now Due to this clash of mvc-annotation and DefaultAnnotationHandlerMapping, I cant use DefaultAnnotationHandlerMapping anymore.
The problem comes down to how can i register my interceptors with tag.
The solution was simple...but hard to find. Posting it so it can be helpful to the other solution seekers..
Use tag and register the interceptor bean in your dispathcer-servlet.xml
example :
<mvc:interceptors>
<!-- This runs for all mappings -->
<bean class="main.com.XXX.MobileDeviceResolverHanlderInterceptor"/>
</mvc:interceptors>
The reason for this behaviour is that two beans of type org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping exist in the application context. Spring resolves the two, but asks only the first for interceptors. To fix this, the following init parameter should be set to the DispatcherServlet
<init-param>
<param-name>detectAllHandlerMappings</param-name>
<param-value>false</param-value>
</init-param>
This makes the dispatcher servlet use only the handlerMapping defined in the x-servlet.xml
It is beyond me why this is the default behaviour. I'm expecting an answer from the spring community.
In my case I can NOT get rid of <mvc:annotation-driven/> as I am using jackson for json support using annotation.
What I tried, moved my all interceptors <mvc:interceptors> in separate "xml" file (interceptor-config.xml) and imported it from my x-dispatcher-servlet.xml
<import resource="interceptor-config.xml"/>
It solve my issue and avoid default 'DefaultAnnotationHandlerMapping' beans my application context.
Rather than creating separate 'xml', you can copy/paste interceptor contents directly in 'x-dispatcher-servlet.xml'.
Following is my interceptor:
<mvc:interceptors>
<mvc:interceptor>
<!-- Intercepting specific URL -->
<mvc:mapping path="/abc/**" />
<bean id= "myInterceptor"
class="xx.xxx.xxx.MyInterceptor" />
</mvc:interceptor>
<mvc:interceptors>
In Spring MVC 3.0 you can use <mvc:interceptors> instead of manual defining the handler mapping.