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.
Related
I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.
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.
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
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.