Related
I have a Spring Boot app setup as a REST api. I now also want to be able to serve simple HTML pages to the client, without the use on any template engine like Thymeleaf. I want access to the HTML pages to fall under the same security constraints setup by Spring Security with the use of WebSecurityConfigurerAdapter, already present in my app.
What I've tried is having a Controller:
#Controller
public class HtmlPageController {
#RequestMapping(value = "/some/path/test", method = RequestMethod.GET)
public String getTestPage() {
return "test.html";
}
}
and placing the test.html file in /resources/test.html or /webapp/WEB-INF/test.html.
Every time I try to access the page at localhost:8080/some/path/test a 404 is returned.
How do I make this work?
Okey so apparently Spring Boot supports this without any additional configuration or controllers.
All I had to do was to place the HTML file in the correct directory /resources/static/some/path/test.html and it can be reached at localhost:8080/some/path/test.html.
In my attempts to change the directory from which the file is served I was unsuccessful. It seems that providing a separate #EnableWebMvc (needed for configuring the resource handlers) breaks the Spring Boot configuration. But I can live with using the default /static directory.
There is a Spring MVC mecanism that exists to provide static resources.
In the config class, overide this method :
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("some/path/*.html")
.addResourceLocations("/static/");
}
And place your html files in the src/main/webapp/static/ folder.
If you request some/path/test.html (note the .html), it will return the test.html file located in static folder.
You can obviously use a different folder or a more sofiticated directory structure.
This way you don't have to create a controller. Note that your config class should implements WebMvcConfigurer.
Your html, js and css files should be under the src/main/resources/static directory. and your return statement you can try removing .html.
#RestController
public class HtmlPageController {
#GetMapping("/some/path/test")
public String getTestPage() {
return "test";
}
}
See tutotrial example how to define html view in Spring MVC configuration
#Bean
public InternalResourceViewResolver htmlViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/html/");
bean.setSuffix(".html");
bean.setOrder(2);
return bean;
}
setOrder is set to 2 because it include also JSP support in example
Also you need to change to return without .html suffix
return "test.html";
I ran the spring-boot-sample-web-static project from here, made this alteration to the pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
And added this class to serve a duplicate page index2.html from the same static folder location:
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class Rester {
#RequestMapping(value = "/rand", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
private RandomObj jsonEndpoint() {
return new RandomObj();
}
#RequestMapping(value = "/tw")
public String somePg() {
return "index2";
}
}
The json url works fine, but when I try to access localhost:8080/tw I get a blank page, and this error in the console:
2017-02-22 15:37:22.076 ERROR 21494 --- [nio-8080-exec-9] o.s.boot.web.support.ErrorPageFilter : Cannot forward to error page for request [/tw] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false
Am I doing something wrong?
Static files should be served from resources, not from a controller.
Spring Boot will automatically add static web resources located within
any of the following directories:
/META-INF/resources/
/resources/
/static/
/public/
refs:
https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
https://spring.io/guides/gs/serving-web-content/
In Spring boot, /META-INF/resources/, /resources/, static/ and public/ directories are available to serve static contents.
So you can create a static/ or public/ directory under resources/ directory and put your static contents there. And they will be accessible by: http://localhost:8080/your-file.ext. (assuming the server.port is 8080)
You can customize these directories using spring.resources.static-locations in the application.properties.
For example:
spring.resources.static-locations=classpath:/custom/
Now you can use custom/ folder under resources/ to serve static files.
This is also possible using Java config in Spring Boot 2:
#Configuration
public class StaticConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/custom/");
}
}
This confugration maps contents of custom directory to the http://localhost:8080/static/** url.
I am using :: Spring Boot :: (v2.0.4.RELEASE) with Spring Framework 5
Spring Boot 2.0 requires Java 8 as a minimum version. Many existing APIs have been updated to take advantage of Java 8 features such as: default methods on interfaces, functional callbacks, and new APIs such as javax.time.
Static Content
By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.
By default, resources are mapped on /** and located on /static directory.
But you can customize the static loactions programmatically inside our web context configuration class.
#Configuration #EnableWebMvc
public class Static_ResourceHandler implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// When overriding default behavior, you need to add default(/) as well as added static paths(/webapp).
// src/main/resources/static/...
registry
//.addResourceHandler("/**") // « /css/myStatic.css
.addResourceHandler("/static/**") // « /static/css/myStatic.css
.addResourceLocations("classpath:/static/") // Default Static Loaction
.setCachePeriod( 3600 )
.resourceChain(true) // 4.1
.addResolver(new GzipResourceResolver()) // 4.1
.addResolver(new PathResourceResolver()); //4.1
// src/main/resources/templates/static/...
registry
.addResourceHandler("/templates/**") // « /templates/style.css
.addResourceLocations("classpath:/templates/static/");
// Do not use the src/main/webapp/... directory if your application is packaged as a jar.
registry
.addResourceHandler("/webapp/**") // « /webapp/css/style.css
.addResourceLocations("/");
// File located on disk
registry
.addResourceHandler("/system/files/**")
.addResourceLocations("file:///D:/");
}
}
http://localhost:8080/handlerPath/resource-path+name
/static /css/myStatic.css
/webapp /css/style.css
/templates /style.css
In Spring every request will go through the DispatcherServlet. To avoid Static file request through DispatcherServlet(Front contoller) we configure MVC Static content.
As #STEEL said static resources should not go through Controller. Thymleaf is a ViewResolver which takes the view name form controller and adds prefix and suffix to View Layer.
As it is written before, some folders (/META-INF/resources/, /resources/, /static/, /public/) serve static content by default, conroller misconfiguration can break this behaviour.
It is a common pitfall that people define the base url of a controller in the #RestController annotation, instead of the #RequestMapping annotation on the top of the controllers.
This is wrong:
#RestController("/api/base")
public class MyController {
#PostMapping
public String myPostMethod( ...) {
The above example will prevent you from opening the index.html. The Spring expects a POST method at the root, because the myPostMethod is mapped to the "/" path.
You have to use this instead:
#RestController
#RequestMapping("/api/base")
public class MyController {
#PostMapping
public String myPostMethod( ...) {
I had to add thymeleaf dependency to pom.xml. Without this dependency Spring boot didn't find static resources.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
You can quickly serve static content in JAVA Spring-boot App via thymeleaf (ref: source)
I assume you have already added Spring Boot plugin apply plugin: 'org.springframework.boot' and the necessary buildscript
Then go ahead and ADD thymeleaf to your build.gradle ==>
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Lets assume you have added home.html at src/main/resources
To serve this file, you will need to create a controller.
package com.ajinkya.th.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomePageController {
#RequestMapping("/")
public String homePage() {
return "home";
}
}
Thats it ! Now restart your gradle server. ./gradlew bootRun
I'm writing a Spring MVC application deployed on Tomcat. See the following minimal, complete, and verifiable example
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { };
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { SpringServletConfig.class };
}
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
Where SpringServletConfig is
#Configuration
#ComponentScan("com.example.controllers")
#EnableWebMvc
public class SpringServletConfig {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
Finally, I have a #Controller in the package com.example.controllers
#Controller
public class ExampleController {
#RequestMapping(path = "/home", method = RequestMethod.GET)
public String example() {
return "index";
}
}
My application's context name is Example. When I send a request to
http://localhost:8080/Example/home
the application responds with an HTTP Status 404 and logs the following
WARN o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher'
I have a JSP resource at /WEB-INF/jsps/index.jsp I expected Spring MVC to use my controller to handle the request and forward to the JSP, so why is it responding with a 404?
This is meant to be a canonical post for questions about this warning message.
Your standard Spring MVC application will serve all requests through a DispatcherServlet that you've registered with your Servlet container.
The DispatcherServlet looks at its ApplicationContext and, if available, the ApplicationContext registered with a ContextLoaderListener for special beans it needs to setup its request serving logic. These beans are described in the documentation.
Arguably the most important, beans of type HandlerMapping map
incoming requests to handlers and a list of pre- and post-processors
(handler interceptors) based on some criteria the details of which
vary by HandlerMapping implementation. The most popular implementation
supports annotated controllers but other implementations exists as
well.
The javadoc of HandlerMapping further describes how implementations must behave.
The DispatcherServlet finds all beans of this type and registers them in some order (can be customized). While serving a request, the DispatcherServlet loops through these HandlerMapping objects and tests each of them with getHandler to find one that can handle the incoming request, represented as the standard HttpServletRequest. As of 4.3.x, if it doesn't find any, it logs the warning that you see
No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName
and either throws a NoHandlerFoundException or immediately commits the response with a 404 Not Found status code.
Why didn't the DispatcherServlet find a HandlerMapping that could handle my request?
The most common HandlerMapping implementation is RequestMappingHandlerMapping, which handles registering #Controller beans as handlers (really their #RequestMapping annotated methods). You can either declare a bean of this type yourself (with #Bean or <bean> or other mechanism) or you can use the built-in options. These are:
Annotate your #Configuration class with #EnableWebMvc.
Declare a <mvc:annotation-driven /> member in your XML configuration.
As the link above describes, both of these will register a RequestMappingHandlerMapping bean (and a bunch of other stuff). However, a HandlerMapping isn't very useful without a handler. RequestMappingHandlerMapping expects some #Controller beans so you need to declare those too, through #Bean methods in a Java configuration or <bean> declarations in an XML configuration or through component scanning of #Controller annotated classes in either. Make sure these beans are present.
If you're getting the warning message and a 404 and you've configured all of the above correctly, then you're sending your request to the wrong URI, one that isn't handled by a detected #RequestMapping annotated handler method.
The spring-webmvc library offers other built-in HandlerMapping implementations. For example, BeanNameUrlHandlerMapping maps
from URLs to beans with names that start with a slash ("/")
and you can always write your own. Obviously, you'll have to make sure the request you're sending matches at least one of the registered HandlerMapping object's handlers.
If you don't implicitly or explicitly register any HandlerMapping beans (or if detectAllHandlerMappings is true), the DispatcherServlet registers some defaults. These are defined in DispatcherServlet.properties in the same package as the DispatcherServlet class. They are BeanNameUrlHandlerMapping and DefaultAnnotationHandlerMapping (which is similar to RequestMappingHandlerMapping but deprecated).
Debugging
Spring MVC will log handlers registered through RequestMappingHandlerMapping. For example, a #Controller like
#Controller
public class ExampleController {
#RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
will log the following at INFO level
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
This describes the mapping registered. When you see the warning that no handler was found, compare the URI in the message to the mapping listed here. All the restrictions specified in the #RequestMapping must match for Spring MVC to select the handler.
Other HandlerMapping implementations log their own statements that should hint to their mappings and their corresponding handlers.
Similarly, enable Spring logging at DEBUG level to see which beans Spring registers. It should report which annotated classes it finds, which packages it scans, and which beans it initializes. If the ones you expected aren't present, then review your ApplicationContext configuration.
Other common mistakes
A DispatcherServlet is just a typical Java EE Servlet. You register it with your typical <web.xml> <servlet-class> and <servlet-mapping> declaration, or directly through ServletContext#addServlet in a WebApplicationInitializer, or with whatever mechanism Spring boot uses. As such, you must rely on the url mapping logic specified in the Servlet specification, see Chapter 12. See also
How are Servlet url mappings in web.xml used?
With that in mind, a common mistake is to register the DispatcherServlet with a url mapping of /*, returning a view name from a #RequestMapping handler method, and expecting a JSP to be rendered. For example, consider a handler method like
#RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
with an InternalResourceViewResolver
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
you might expect the request to be forwarded to a JSP resource at the path /WEB-INF/jsps/example-view-name.jsp. This won't happen. Instead, assuming a context name of Example, the DisaptcherServlet will report
No mapping found for HTTP request with URI [/Example/WEB-INF/jsps/example-view-name.jsp] in DispatcherServlet with name 'dispatcher'
Because the DispatcherServlet is mapped to /* and /* matches everything (except exact matches, which have higher priority), the DispatcherServlet would be chosen to handle the forward from the JstlView (returned by the InternalResourceViewResolver). In almost every case, the DispatcherServlet will not be configured to handle such a request.
Instead, in this simplistic case, you should register the DispatcherServlet to /, marking it as the default servlet. The default servlet is the last match for a request. This will allow your typical servlet container to chose an internal Servlet implementation, mapped to *.jsp, to handle the JSP resource (for example, Tomcat has JspServlet), before trying with the default servlet.
That's what you're seeing in your example.
I resolved my issue when in addition to described before:`
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
added tomcat-embed-jasper:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
`
from: JSP file not rendering in Spring Boot web application
In my case, I was following the Interceptors Spring documentation for version 5.1.2 (while using Spring Boot v2.0.4.RELEASE) and the WebConfig class had the annotation #EnableWebMvc, which seemed to be conflicting with something else in my application that was preventing my static assets from being resolved correctly (i.e. no CSS or JS files were being returned to the client).
After trying a lot of different things, I tried removing the #EnableWebMvc and it worked!
Edit: Here's the reference documentation that says you should remove the #EnableWebMvc annotation
Apparently in my case at least, I'm already configuring my Spring application (although not by using web.xml or any other static file, it's definitely programmatically), so it was a conflict there.
Try to amend your code with the following change on your config file. Java config is used instead of application.properties.
Do not forget to enable configuration in configureDefaultServletHandling method.
WebMvcConfigurerAdapter class is deprecated, so we use WebMvcConfigurer interface.
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
I use gradle, your should have the following dependencies in pom.xml:
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.0.RELEASE'
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.35'
}
I came across another reason for the same error. This could also be due to the class files not generated for your controller.java file. As a result of which the the dispatcher servlet mentioned in web.xml is unable to map it to the appropriate method in the controller class.
#Controller
Class Controller{
#RequestMapping(value="/abc.html")//abc is the requesting page
public void method()
{.....}
}
In eclipse under Project->select clean ->Build Project.Do give a check if the class file has been generated for the controller file under builds in your workspace.
Clean your server. Maybe delete the server and add the project once again and Run.
Stop the Tomcat server
Right click the server and select "Clean"
Right click server again and select "Clean Tomcat Work Directory"
In my case using a tutorial for SpringBoot(2.7.3) RestController, startup failed with java.lang.ClassNotFoundException: javax.validation.ParameterNameProvider
I thought that Spring REST does not use WebMvc so I removed 'spring-boot-starter-web' and that resolved the startup problem. However, POST requests failed with the '404 Not Found' issue described here and spent several hours experimenting with
server.servlet.context-path
#RestController vs #Controller etc, and
SecurityConfig options
I finally resolved the 404 issue by
restoring dependency 'spring-boot-starter-web'
adding dependency
javax.validation
validation-api
and after undoing my 101 debug hacks it worked.
Very painful because even with root logger at DEBUG, there were no server-side logs to help.
For me, I found that my target classes were generated in a folder pattern not same as source. This is possibly in eclipse I add folders for containing my controllers and not add them as packages. So I ended up defining incorrect path in spring config.
My target class was generating classes under app and I was referring to com.happy.app
<context:annotation-config />
<context:component-scan
base-package="com.happy.app"></context:component-scan>
I added packages (not folders) for com.happy.app and moved the files from folders to packages in eclipse and it resolved the issue.
In my case, I was playing around with import of secondary java config files into a main java config file. While making secondary config files, I had changed the name of the main config class, but I had failed to update the name in web.xml. So, every time that I had restarted my tomcat server, I was not seeing mapping handlers noted in the Eclipse IDE console, and when I tried to navigate to my home page I was seeing this error:
Nov 1, 2019 11:00:01 PM org.springframework.web.servlet.PageNotFound
noHandlerFound WARNING: No mapping found for HTTP request with URI
[/webapp/home/index] in DispatcherServlet with name 'dispatcher'
The fix was to update the web.xml file so that the old name "WebConfig" would be instead "MainConfig", simply renaming it to reflect the latest name of the main java config file (where "MainConfig" is arbitrary and the words "Web" and "Main" used here are not a syntax requirement). MainConfig was important, because it was the file that did the component scan for "WebController", my spring mvc controller class that handles my web requests.
#ComponentScan(basePackageClasses={WebController.class})
web.xml had this:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.lionheart.fourthed.config.WebConfig
</param-value>
</init-param>
web.xml file now has:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.lionheart.fourthed.config.MainConfig
</param-value>
</init-param>
Now I am seeing the mapping in the console window:
INFO: Mapped "{[/home/index],methods=[GET]}" onto public
org.springframework.web.servlet.ModelAndView
com.lionheart.fourthed.controller.WebController.gotoIndex()
And my web page is loading again.
In my case, I had created Config.java (class) and also config.xml and mapping was done partially in both of them. And since config.java uses #Configuration annotation , it was considered priority. And was not considering config.xml.
If anyone gets in trouble like this , just delete config.java with annotation and try to keep config.xml , it works fine.
For me, the issue was hidden in the web.xml file.
Inside the servlet tag, you'll find:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/todo-servlet.xml</param-value>
</init-param>
Make sure that in the <param-value> you have kept the correct location of the dispatcher servlet (aka Front Controller).
I had kept an incorrect location, hence I was able to view the homepage but all other pages were giving HTTP 404 error.
I had same problem as **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**
After I analyzed for 2 to 4 days I found out the root cause. Class files was not generated after I run the project. I clicked the project tab.
Project-->CloseProject-->OpenProject-->Clean-->Build project
Class files for source code have been generated. It solved my problem. To check whether class files have been generated or not, Please check the Build folder in your project folder.
So the problem can be as simple as an additional space in the path of the project. Make sure that there is no space in the path which took me quite some time to solve.
I have a Spring (4) MVC servlet running on Tomcat 8 in Eclipse. When I start tomcat, there are no errors in the console and all the correct request mappings for my controllers are logged. If I try to access localhost:8080/app/login my controller method executes (checked via debugging), but I get a 404 page with the following:
message /app/WEB-INF/jsp/login.jsp
description The requested resource is not available.
My project has the following directory structure:
project-root
|-src
|-WebContent
|-WEB-INF
|-jsp
|-login.jsp
My configuration class:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "com.example")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/jsp/", ".jsp").viewClass(JstlView.class);
}
//Other stuff
}
Controller:
#Controller
#RequestMapping("/login")
public class AuthnRequestController {
#RequestMapping(value = "", method = RequestMethod.GET)
public ModelAndView getLoginPage() {
return new ModelAndView("login");
}
//Other stuff
}
The application was working fine in the past, but I was screwing around with my workspace/projects working on something else, and am unable to get this working again now that I'm coming back to it.
AFAIK, By default in a maven war project the jsp files are expected under /src/main/resources/. Since you have given a jsp file prefix of /WEB-INF/jsp/ in your config, please try moving the jsp files to the below location.
/src/main/webapp/WEB-INF/jsp/
Assumptions:
a mapping to root/WebContent is not provided in Web deployment assembly.
a mapping to /src/main/webapp is present in Web deployment assembly.
your eclipse is using maven war plugin
I'm writing a Spring MVC application deployed on Tomcat. See the following minimal, complete, and verifiable example
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { };
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { SpringServletConfig.class };
}
protected String[] getServletMappings() {
return new String[] { "/*" };
}
}
Where SpringServletConfig is
#Configuration
#ComponentScan("com.example.controllers")
#EnableWebMvc
public class SpringServletConfig {
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
Finally, I have a #Controller in the package com.example.controllers
#Controller
public class ExampleController {
#RequestMapping(path = "/home", method = RequestMethod.GET)
public String example() {
return "index";
}
}
My application's context name is Example. When I send a request to
http://localhost:8080/Example/home
the application responds with an HTTP Status 404 and logs the following
WARN o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher'
I have a JSP resource at /WEB-INF/jsps/index.jsp I expected Spring MVC to use my controller to handle the request and forward to the JSP, so why is it responding with a 404?
This is meant to be a canonical post for questions about this warning message.
Your standard Spring MVC application will serve all requests through a DispatcherServlet that you've registered with your Servlet container.
The DispatcherServlet looks at its ApplicationContext and, if available, the ApplicationContext registered with a ContextLoaderListener for special beans it needs to setup its request serving logic. These beans are described in the documentation.
Arguably the most important, beans of type HandlerMapping map
incoming requests to handlers and a list of pre- and post-processors
(handler interceptors) based on some criteria the details of which
vary by HandlerMapping implementation. The most popular implementation
supports annotated controllers but other implementations exists as
well.
The javadoc of HandlerMapping further describes how implementations must behave.
The DispatcherServlet finds all beans of this type and registers them in some order (can be customized). While serving a request, the DispatcherServlet loops through these HandlerMapping objects and tests each of them with getHandler to find one that can handle the incoming request, represented as the standard HttpServletRequest. As of 4.3.x, if it doesn't find any, it logs the warning that you see
No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName
and either throws a NoHandlerFoundException or immediately commits the response with a 404 Not Found status code.
Why didn't the DispatcherServlet find a HandlerMapping that could handle my request?
The most common HandlerMapping implementation is RequestMappingHandlerMapping, which handles registering #Controller beans as handlers (really their #RequestMapping annotated methods). You can either declare a bean of this type yourself (with #Bean or <bean> or other mechanism) or you can use the built-in options. These are:
Annotate your #Configuration class with #EnableWebMvc.
Declare a <mvc:annotation-driven /> member in your XML configuration.
As the link above describes, both of these will register a RequestMappingHandlerMapping bean (and a bunch of other stuff). However, a HandlerMapping isn't very useful without a handler. RequestMappingHandlerMapping expects some #Controller beans so you need to declare those too, through #Bean methods in a Java configuration or <bean> declarations in an XML configuration or through component scanning of #Controller annotated classes in either. Make sure these beans are present.
If you're getting the warning message and a 404 and you've configured all of the above correctly, then you're sending your request to the wrong URI, one that isn't handled by a detected #RequestMapping annotated handler method.
The spring-webmvc library offers other built-in HandlerMapping implementations. For example, BeanNameUrlHandlerMapping maps
from URLs to beans with names that start with a slash ("/")
and you can always write your own. Obviously, you'll have to make sure the request you're sending matches at least one of the registered HandlerMapping object's handlers.
If you don't implicitly or explicitly register any HandlerMapping beans (or if detectAllHandlerMappings is true), the DispatcherServlet registers some defaults. These are defined in DispatcherServlet.properties in the same package as the DispatcherServlet class. They are BeanNameUrlHandlerMapping and DefaultAnnotationHandlerMapping (which is similar to RequestMappingHandlerMapping but deprecated).
Debugging
Spring MVC will log handlers registered through RequestMappingHandlerMapping. For example, a #Controller like
#Controller
public class ExampleController {
#RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
will log the following at INFO level
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
This describes the mapping registered. When you see the warning that no handler was found, compare the URI in the message to the mapping listed here. All the restrictions specified in the #RequestMapping must match for Spring MVC to select the handler.
Other HandlerMapping implementations log their own statements that should hint to their mappings and their corresponding handlers.
Similarly, enable Spring logging at DEBUG level to see which beans Spring registers. It should report which annotated classes it finds, which packages it scans, and which beans it initializes. If the ones you expected aren't present, then review your ApplicationContext configuration.
Other common mistakes
A DispatcherServlet is just a typical Java EE Servlet. You register it with your typical <web.xml> <servlet-class> and <servlet-mapping> declaration, or directly through ServletContext#addServlet in a WebApplicationInitializer, or with whatever mechanism Spring boot uses. As such, you must rely on the url mapping logic specified in the Servlet specification, see Chapter 12. See also
How are Servlet url mappings in web.xml used?
With that in mind, a common mistake is to register the DispatcherServlet with a url mapping of /*, returning a view name from a #RequestMapping handler method, and expecting a JSP to be rendered. For example, consider a handler method like
#RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
with an InternalResourceViewResolver
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
you might expect the request to be forwarded to a JSP resource at the path /WEB-INF/jsps/example-view-name.jsp. This won't happen. Instead, assuming a context name of Example, the DisaptcherServlet will report
No mapping found for HTTP request with URI [/Example/WEB-INF/jsps/example-view-name.jsp] in DispatcherServlet with name 'dispatcher'
Because the DispatcherServlet is mapped to /* and /* matches everything (except exact matches, which have higher priority), the DispatcherServlet would be chosen to handle the forward from the JstlView (returned by the InternalResourceViewResolver). In almost every case, the DispatcherServlet will not be configured to handle such a request.
Instead, in this simplistic case, you should register the DispatcherServlet to /, marking it as the default servlet. The default servlet is the last match for a request. This will allow your typical servlet container to chose an internal Servlet implementation, mapped to *.jsp, to handle the JSP resource (for example, Tomcat has JspServlet), before trying with the default servlet.
That's what you're seeing in your example.
I resolved my issue when in addition to described before:`
#Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
added tomcat-embed-jasper:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
`
from: JSP file not rendering in Spring Boot web application
In my case, I was following the Interceptors Spring documentation for version 5.1.2 (while using Spring Boot v2.0.4.RELEASE) and the WebConfig class had the annotation #EnableWebMvc, which seemed to be conflicting with something else in my application that was preventing my static assets from being resolved correctly (i.e. no CSS or JS files were being returned to the client).
After trying a lot of different things, I tried removing the #EnableWebMvc and it worked!
Edit: Here's the reference documentation that says you should remove the #EnableWebMvc annotation
Apparently in my case at least, I'm already configuring my Spring application (although not by using web.xml or any other static file, it's definitely programmatically), so it was a conflict there.
Try to amend your code with the following change on your config file. Java config is used instead of application.properties.
Do not forget to enable configuration in configureDefaultServletHandling method.
WebMvcConfigurerAdapter class is deprecated, so we use WebMvcConfigurer interface.
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/", ".jsp");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
I use gradle, your should have the following dependencies in pom.xml:
dependencies {
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.0.RELEASE'
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.35'
}
I came across another reason for the same error. This could also be due to the class files not generated for your controller.java file. As a result of which the the dispatcher servlet mentioned in web.xml is unable to map it to the appropriate method in the controller class.
#Controller
Class Controller{
#RequestMapping(value="/abc.html")//abc is the requesting page
public void method()
{.....}
}
In eclipse under Project->select clean ->Build Project.Do give a check if the class file has been generated for the controller file under builds in your workspace.
Clean your server. Maybe delete the server and add the project once again and Run.
Stop the Tomcat server
Right click the server and select "Clean"
Right click server again and select "Clean Tomcat Work Directory"
In my case using a tutorial for SpringBoot(2.7.3) RestController, startup failed with java.lang.ClassNotFoundException: javax.validation.ParameterNameProvider
I thought that Spring REST does not use WebMvc so I removed 'spring-boot-starter-web' and that resolved the startup problem. However, POST requests failed with the '404 Not Found' issue described here and spent several hours experimenting with
server.servlet.context-path
#RestController vs #Controller etc, and
SecurityConfig options
I finally resolved the 404 issue by
restoring dependency 'spring-boot-starter-web'
adding dependency
javax.validation
validation-api
and after undoing my 101 debug hacks it worked.
Very painful because even with root logger at DEBUG, there were no server-side logs to help.
For me, I found that my target classes were generated in a folder pattern not same as source. This is possibly in eclipse I add folders for containing my controllers and not add them as packages. So I ended up defining incorrect path in spring config.
My target class was generating classes under app and I was referring to com.happy.app
<context:annotation-config />
<context:component-scan
base-package="com.happy.app"></context:component-scan>
I added packages (not folders) for com.happy.app and moved the files from folders to packages in eclipse and it resolved the issue.
In my case, I was playing around with import of secondary java config files into a main java config file. While making secondary config files, I had changed the name of the main config class, but I had failed to update the name in web.xml. So, every time that I had restarted my tomcat server, I was not seeing mapping handlers noted in the Eclipse IDE console, and when I tried to navigate to my home page I was seeing this error:
Nov 1, 2019 11:00:01 PM org.springframework.web.servlet.PageNotFound
noHandlerFound WARNING: No mapping found for HTTP request with URI
[/webapp/home/index] in DispatcherServlet with name 'dispatcher'
The fix was to update the web.xml file so that the old name "WebConfig" would be instead "MainConfig", simply renaming it to reflect the latest name of the main java config file (where "MainConfig" is arbitrary and the words "Web" and "Main" used here are not a syntax requirement). MainConfig was important, because it was the file that did the component scan for "WebController", my spring mvc controller class that handles my web requests.
#ComponentScan(basePackageClasses={WebController.class})
web.xml had this:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.lionheart.fourthed.config.WebConfig
</param-value>
</init-param>
web.xml file now has:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.lionheart.fourthed.config.MainConfig
</param-value>
</init-param>
Now I am seeing the mapping in the console window:
INFO: Mapped "{[/home/index],methods=[GET]}" onto public
org.springframework.web.servlet.ModelAndView
com.lionheart.fourthed.controller.WebController.gotoIndex()
And my web page is loading again.
In my case, I had created Config.java (class) and also config.xml and mapping was done partially in both of them. And since config.java uses #Configuration annotation , it was considered priority. And was not considering config.xml.
If anyone gets in trouble like this , just delete config.java with annotation and try to keep config.xml , it works fine.
For me, the issue was hidden in the web.xml file.
Inside the servlet tag, you'll find:
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/todo-servlet.xml</param-value>
</init-param>
Make sure that in the <param-value> you have kept the correct location of the dispatcher servlet (aka Front Controller).
I had kept an incorrect location, hence I was able to view the homepage but all other pages were giving HTTP 404 error.
I had same problem as **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**
After I analyzed for 2 to 4 days I found out the root cause. Class files was not generated after I run the project. I clicked the project tab.
Project-->CloseProject-->OpenProject-->Clean-->Build project
Class files for source code have been generated. It solved my problem. To check whether class files have been generated or not, Please check the Build folder in your project folder.
So the problem can be as simple as an additional space in the path of the project. Make sure that there is no space in the path which took me quite some time to solve.