I encounter a strange problem in my Spring MVC Controller.
I have four pages in my webapp folder
#Controller
public class WelcomeController {
#RequestMapping(value="/wodi/welcome",method=RequestMethod.GET)
public String welcome(){
return "redirect:/pages/webwelcome.html";
}
}
Just now, it worked fine to find the page http://localhost:8080/pages/webwelcome.html, but now I have the error that the browser says:
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'GET' not supported
I have no idea what I did that influence it.
I read WARN : org.springframework.web.servlet.PageNotFound - Request method 'GET' not supported
But this is not the same case as mine since I am using "GET" method.
Below is my Application.java to Boot the Spring app
#Configuration
#EnableAutoConfiguration
#ComponentScan({"hello","wodinow.weixin.jaskey"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
#Bean
public CommandService commandService(){
return CommandService.getInstance();
}
}
In my case everything was ok. But i have a problem in a controller
that was my problem
#RequestMapping( method = RequestMethod.GET)
y change for this:
#RequestMapping(value = "/usuario", method = RequestMethod.GET)
and it works
Unless you put really weird thing in your config, the (embedded) container should be able to serve static content or JSP that are not under WEB-INF. The only use case where problem could happen would be if you map Spring DispatcherServlet to /* of forget to allow the serving of static resources.
You will find more references on the serving of static resources on my other post Match for root url and serving of static resources.
But usually, in a controller you do not redirect to a HTML page, but give the name of a view and the view resolver finds the appropriate view.
The ViewResolver registered in your application configuration is responsible for resolving pages from a given URL.
Example: Config for resolving URLs like /welcome to corresponding JSP file /pages/welcome.jsp
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/pages/" />
<property name="suffix" value=".jsp" />
</bean>
However, JSP pages are dynamic and need special handling. For static resources like plain html pages, it is sufficient to set up a static mapping for your pages folder.
<mvc:resources location="/pages/" mapping="/**" />
This would result in all resources in the folder /pages being mapped to URLs starting with "/". For example: /pages/welcome.html would be accessible by http://yourdomain/welcome.html
And if you want to set up a view resolver for one specific URL you can use a view-controller in the configuration:
<mvc:view-controller path="/wodi/welcome" view-name="/pages/webwelcome.html"/>
UPDATE:
As you are using Spring Boot with #EnableAutoConfiguration you are already using the second method. Here you can see a code snippet from the AutoConfiguration implementation. It shows that a ResourceHandler is added to URL /** with some predefined locations.
If you want custom URL mappings, I suggest you use one of the above mentioned methods in a plain Spring MVC configuration. Here is the documentation for enabling Spring MVC config. You can decide yourself if you use xml or annotation based configuration.
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.
I'm attempting to forward a request received from one Controller to a second Controller using Spring's "forward:" prefix. However, instead of forwarding the request on to the second Controller as I would expect, the request is instead handled by the first Controller over and over again (until I receive a StackOverflowError). This made me think it might be treating the "/app/pong" as a relative path of some sort, but I'm not sure why this would be the case. Am I misunderstanding how "forward:" is supposed to work? Is there something that I'm missing here?
Controllers:
#Controller
public class ControllerOne {
#RequestMapping(value = "/ping", method = RequestMethod.GET)
public String doPing(HttpServletRequest request) {
log.debug("Ping?");
return "forward:/app/pong";
}
}
#Controller
public class ControllerTwo {
#RequestMapping(value = "/pong", method = RequestMethod.GET)
public String doPong(HttpServletRequest request) {
log.debug("Pong!");
return "pong";
}
}
servlet-mapping:
<servlet-mapping>
<servlet-name>test-servlet</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
view resolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
I think the problem is about the InternalResourceViewResolver that you configed. Because the inner logic of this viewResolver, it will do a forward automatically, you can config another kinds of viewResolver: UrlBasedViewResolver .
Below is the information I found in official document:
It is also possible to use a special forward: prefix for view names
that are ultimately resolved by UrlBasedViewResolver and subclasses.
This creates an InternalResourceView (which ultimately does a
RequestDispatcher.forward()) around the rest of the view name, which
is considered a URL. Therefore, this prefix is not useful with
InternalResourceViewResolver and InternalResourceView (for JSPs for example). But the prefix can be helpful when you are primarily
using another view technology, but still want to force a forward of a
resource to be handled by the Servlet/JSP engine. (Note that you may
also chain multiple view resolvers, instead.)
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"
We're currently adding some new features to an old webapp which was using only JSP without any framework for the front. We have added Spring recently, and we would like to autowire our beans in our modified JSP, while not rewriting everything to use SpringMVC, Struts2 or Tapestry5.
We're using autowiring by type, so it leads to get some code like this in the JSP, while previously getting the web application context ( as "wap") :
MyDao myDao = (MyDao) wap.getBeansOfType(MyDao.class).values().toArray()[0];
We would like not to use such a code but rather automagically inject our beans directly in our JSPs as we would in a business bean using #Autowired annotation.
In fact we're looking to the cleanest ways to inject our beans in our JSPs. What do you use ?
You can use Spring's ContextExposingHttpServletRequest:
HttpServletRequest decorator that
makes all Spring beans in a given
WebApplicationContext accessible as
request attributes, through lazy
checking once an attribute gets
accessed.
This would require your controller code to wrap the original HttpServletRequest in a ContextExposingHttpServletRequest, and then forward that to the JSP. It can either expose specific named beans, or every bean in the context.
Of course, this just shifts the problem from your JSPs to your controller code, but that's perhaps a more manageable problem.
You can't use #Autowired directly because both your jsps and servlets are instantiated by the servlet conainer. So they are not part of the spring context and hence their dependencies aren't injected.
You can:
move all code that to pure servlets, rather than in jsps - leave only presentation in the jsps.
use #Configurable on your servlets (and add a javaagent, as described in the linked docs)
Another way, is to make the servlet part of the current context manually. This is possible in both jsps and servlets:
public void init() {
WebApplicationContext ctx = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());
AutowireCapableBeanFactory bf = ctx.getAutowireCapableBeanFactory();
bf.autowireBean(this);
}
This will resolve the #Autowired annotated dependencies.
Now, I'm not sure whether servlet containers are required to use only one instance of a servlet class. If not, you'd better place the above code in a getter-method for the dependency (getDao()) and if the #Autowired property is null (i.e. another instance of the servlet-class is used by the container) - perform the above operation.
That all said, really consider using a web framework (any of the ones you listed). Having logic in jsps is completely wrong, hard to support, hard to read, etc.
What about overriding jspInit() method and adding Autowiring support:
<%# page import="com.example.ExampleService"%>
<%# page import="org.springframework.beans.factory.annotation.Value"%>
<%# page import="org.springframework.beans.factory.annotation.Autowired"%>
<%# page import="org.springframework.web.context.support.SpringBeanAutowiringSupport"%>
<%!
public void jspInit()
{
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
getServletContext());
}
#Value("${example.property}")
private String someField;
#Autowired
private ExampleService exampleService;
%>
<% final Object data = exampleService.getSomething(someField); %>
I doubt that there is a clean way to inject dependencies into a JSP.
I think that the clean solution would be to start refactoring your code to get the business logic out of the JSPs, using either SpringMVC or one of the alternatives you cited.
Start with one or more minimalist controllers that simply pass the request to the JSPs with the injected beans as attributes; #skaffman's answer gives one way to do that, or you could do it more selectively. Then progressively migrate code out of the JSPs and into the controllers.
This isn't autowired, but Spring can expose your bean names into the request context, you just need to configure it in the viewResolver.
From: https://raibledesigns.com/rd/entry/spring_mvc_jstlview_and_exposecontextbeansasattributes
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>