Serving static files with spaces in file names with Spring Boot - java

I've found a very strange behavior in Spring Boot when trying to serve static files with spaces (or any other special chars, like accents) in file names.
I'm using Spring Boot 2.6.1 with Spring Web MVC and the following customization:
#Configuration
public class MyConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/repo/**")
.addResourceLocations("file:///srv/intranet/repo/");
}
}
I have two files under /srv/intranet/repo, named foo.txt and foo bar.txt (note the space in the second file name).
When I start my application, I can access the first file at http://localhost:8080/repo/foo.txt. But I cannot access the second file (the one with the space in the file name) at http://localhost:8080/repo/foo%20bar.txt (I get a 404 error).
BUT if I put the file foo bar.txt under src/main/resources/static, then I can acces the file at http://localhost:8080/foo%20bar.txt.
I'm aware that Spring Boot configures several directories by default to serve static content (one of them being classpath:/static/), so I'm wondering: what is the difference between the preconfigured directories and the one I'm adding in my #Configuration class via addResourceHandler().addResourceLocations()? Am I missing some details when adding the new resourceHandler?
WORKAROUND
You can set the following property in your application.properties (or equivalent .yml) to get the old behavior in Spring Boot (pre-v2.6.0):
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
UPDATE
I believe this is probably a bug in PathPattern, which replaces AntPathMatcher, and was introduced in Spring Framework 5.3 and adopted in Spring Boot 2.6.0. I submitted a bug report.
UPDATE (2022-06-04)
The bug has been fixed. The fix will be included in Spring Framework 5.3.21.

I have found a workaround for this issue.
Just add the following in your Spring Boot configuration file application.properties:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
The documentation for this property states that ant-path-matcher is the default value, but it is not. The source code shows that the default value is path-pattern-parser. I submitted an issue.

I have following configuration with Spring Boot 2.6.1 and it successfully loads file with a space in name.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.example.sw" })
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/files/**").addResourceLocations("file:D:/img/");
}
}
I have an image with name "SO 1.png" (note the space). When hitting the application I get the image
You can probably troubleshoot by putting Spring web on TRACE level where it emits below information while serving the file.
logging.level.org.springframework.web.servlet=TRACE
Below are the logs
2021-12-07 14:24:24.544 TRACE 17200 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/files/SO%201.png", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2021-12-07 14:24:24.568 TRACE 17200 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to HandlerExecutionChain with [ResourceHttpRequestHandler [URL [file:D:/img/]]] and 3 interceptors
2021-12-07 14:24:24.580 TRACE 17200 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler : Applying default cacheSeconds=-1
2021-12-07 14:24:24.626 TRACE 17200 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2021-12-07 14:24:24.626 DEBUG 17200 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK, headers={masked}

in Spring 5 a new PathPatternParser was introduced
try replacing
registry.addResourceHandler("/files/**")
with:
registry.addResourceHandler("/files/{*path}")

Related

How to use the right Spring Security configuration during a MockMvc test

I am trying to test the access of one of my #RestController which is secured by a custom Spring Security configuration. My use case is the following: A HTTP GET to /someEndpoint is secured with authentification, but a HTTP POST request to the same endpoint is not secured. It's working fine when I boot application and test it with my frontend or Postman.
Now I am trying to write tests with MockMvc with the security configuration. I already made it through a lot of answers on StackOverflow, but nothing helped me.
My test setup looks like the following:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = MyController.class)
#WebAppConfiguration
#ContextConfiguration
public class AssessmentControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void init() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.alwaysDo(print())
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
// some test methods
}
With this setup all my endpoints are secured and even a HTTP POST is returning a 401 instead of 201. I also enabled the debug log for security and in the debug logs it says that the test uses the default configure(HttpSecurity) and I can't find any of my AntMatchers in the logs:
2018-07-04 19:20:02.829 DEBUG 2237 --- [ main] s.s.c.a.w.c.WebSecurityConfigurerAdapter : Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).
2018-07-04 19:20:03.097 DEBUG 2237 --- [ main] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'authenticated', for org.springframework.security.web.util.matcher.AnyRequestMatcher#1
2018-07-04 19:20:03.127 DEBUG 2237 --- [ main] o.s.s.w.a.i.FilterSecurityInterceptor : Validated configuration attributes
2018-07-04 19:20:03.130 DEBUG 2237 --- [ main] o.s.s.w.a.i.FilterSecurityInterceptor : Validated configuration attributes
2018-07-04 19:20:03.161 INFO 2237 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#5a75ec37, org.springframework.security.web.context.SecurityContextPersistenceFilter#3f736a16, org.springframework.security.web.header.HeaderWriterFilter#529c2a9a, org.springframework.security.web.csrf.CsrfFilter#7f93dd4e, org.springframework.security.web.authentication.logout.LogoutFilter#707b1a44, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#26c89563, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#1e0a864d, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#22ebccb9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#53abfc07, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#4aa21f9d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#2c05ff9d, org.springframework.security.web.session.SessionManagementFilter#26bbe604, org.springframework.security.web.access.ExceptionTranslationFilter#4375b013, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#a96d56c]
2018-07-04 19:20:03.236 INFO 2237 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2018-07-04 19:20:03.237 INFO 2237 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
Is it in general possible to use my concrete Spring Security configuration during a MockMvc test or do I have to boot the whole Spring context during the test with #SpringBootTest ? I am using (Spring Boot 2.0.3.RELEASE with Java 1.8)
Thanks in advance!
With the spring-boot 2.x it is not possible to switch of security with a property anymore. You have to write an own SecurityConfiguration which has to be added to your test context. This security config should allow any request without authentication.
#Configuration
#EnableWebSecurity
public class TestSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception{
web.debug(true);
}
}
test class annotation:
#ContextConfiguration(classes = { ..., TestSecurityConfiguration.class })
public class MyTests {...

get name of the file loaded by #PropertySource

Is there anyway I can know in my program, the full path of file loaded through #PropertySource annotation of Spring.
I need it to show in logs so that one can know which property file is being used in the application
This information is logged already by StandardServletEnvironment. You can set log level to DEBUG for org.springframework.web.context.support.StandardServletEnvironment class to show details in your logs.
If you use spring-boot you can simply add following line into your application.properties file.
logging.level.org.springframework.web.context.support.StandardServletEnvironment = DEBUG
Below seems to be working, though I am not sure if the instance is always of type ConfigurableEnvironment
#Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent>{
#Autowired
private Environment env;
private static final Logger log = LoggerFactory.getLogger(MyListener.class);
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(env instanceof ConfigurableEnvironment){
MutablePropertySources propertySources = ((ConfigurableEnvironment)env).getPropertySources();
for(PropertySource ps : propertySources){
log.info(ps.getName()); //if only file based needed then check if instanceof ResourcePropertySource
}
}
}
}
Edit: don't really need all this. As already answered by Selim, enabling the proper logs does the trick
log4j.logger.org.springframework.core.env.MutablePropertySources=DEBUG
in current ('21) versions of Spring Boot, neither of the two above suggestions for the logging level seem to work. moreover - if the file is actually NOT loaded, because it is NOT found or for whatever other reason, nothing is printed anyway.
at the moment when i have my ROOT logger set to DEBUG (logging.level.root=DEBUG in application.properties), the only thing I see in the log file, when the file is loaded correctly and the #Value annotated property is resolved successfully is:
2021-07-23 11:06:10.299 DEBUG 16776 --- [ restartedMain]
o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of
singleton bean 'bahblahService'
2021-07-23 11:06:10.302 DEBUG 16776 --- [ restartedMain]
o.s.c.e.PropertySourcesPropertyResolver : Found key
'blahblah.username' in PropertySource 'class path
resource [custom-local.properties]' with value of type String

Using Spring Boot & Spring Integration with database backed Configuration

For spring boot + integration application, I'm attempting to load configuration from database, allow it to be accessible to Spring's Environment & inject-able via #Value annotation and be override-able by externalized configurations as described in the spring boot reference documentation under the Externalized Configuration Section.
The problem I'm having is that my spring Integration XML contains ${input} property placeholders that can not be resolved, because I can't get the database backed configuration to be loaded before Spring attempts to load the XML configurations.
The entry point to the application:
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
How database configuration would be loaded:
#Configuration
public class DbPropertiesConfig {
#Autowired
private org.springframework.core.env.Environment env;
#PostConstruct
public void initializeDatabasePropertySourceUsage() {
MutablePropertySources propertySources = ((ConfigurableEnvironment) env).getPropertySources();
try {
// The below code will be replace w/ code to load config from DB
Map<String,Object> map = new HashMap<>();
map.put("input-dir","target/input");
map.put("output-dir","target/output");
DbPropertySource dbPropertySource = new DbPropertySource("si",map);
propertySources.addLast(dbPropertySource);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
How Spring Integration config is loaded:
#Profile("IN")
#Configuration
#ImportResource({"si-common-context.xml","si-input-context.xml"})
public class SiInputAppConfig {
}
The Spring Integration XML configurationsi-input-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd" default-lazy-init="true">
<int-file:inbound-channel-adapter channel="input2" directory="${input-dir}" filename-pattern="*">
<int:poller fixed-rate="500"/>
</int-file:inbound-channel-adapter>
<int:service-activator input-channel="input2" ref="sampleEndpoint" method="hello" output-channel="output2"/>
<int:channel id="output2"/>
<int-file:outbound-channel-adapter channel="output2" directory="${output-dir}"/>
</beans:beans>
The error I get:
2015-10-28 17:22:18.283 INFO 3816 --- [ main] o.s.b.f.xml.XmlBeanDefinitionReader : Loading XML bean definitions from class path resource [si-common-context.xml]
2015-10-28 17:22:18.383 INFO 3816 --- [ main] o.s.b.f.xml.XmlBeanDefinitionReader : Loading XML bean definitions from class path resource [si-mail-in-context.xml]
2015-10-28 17:22:18.466 INFO 3816 --- [ main] o.s.b.f.config.PropertiesFactoryBean : Loading properties file from URL [jar:file:/C:/Users/xxx/.m2/repository/org/springframework/integration/spring-integration-core/4.1.6.RELEASE/spring-integration-core-4.1.6.RELEASE.jar!/META-INF/spring.integration.default.properties]
2015-10-28 17:22:18.471 INFO 3816 --- [ main] o.s.i.config.IntegrationRegistrar : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2015-10-28 17:22:18.604 INFO 3816 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; ...
2015-10-28 17:22:18.930 WARN 3816 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean#0.source' defined in null: Could not resolve placeholder 'si.in-input' in string value "${si.in-input}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'input-dir' in string value "${input-dir}" ...
Spring loads DbPropertiesConfig after XML configuration is attempted to be loaded.
How can I resolve this issue?
Thanks in advance
AP
Yes, I can confirm by my home tests that XML definitions are loaded before the annotation stuff. It's enough complicated to determine why it is, but I'm sure that #Import* resources are more important by premise than an internal #Configuration logic.
Therefore your #PostConstruct isn't good for mix with XML.
One solution is to move all Spring Integration configuration to the Annotation style and even consider to use Spring Integration Java DSL.
Another solution is to follow with Spring Boot's Externalized Configuration recommendation:
Default properties (specified using SpringApplication.setDefaultProperties).
That means you have to read properties from your DB before the starting Spring Application. Yes, if you were going to use DataSource on the matter from the same application, it won't be possible. From other side let's take a look to your goal one more time! You are going to load properties for the application from DB as an external configuration, so, what is the point to do that from the application itself? Of course, it would be more safe to load and feed properties before application start.
Hope I am clear.

how to configure 'dispatcherServlet' load on startup by spring boot?

I use spring-boot-starter-parent as parent and add spring-boot-starter-web as denpendency.
By add the #SpringBootApplication annotation, it works.
But DispatcherServlet need initialization
Initializing servlet 'dispatcherServlet'
FrameworkServlet 'dispatcherServlet': initialization started
Using MultipartResolver [org.springframework.web.multipart.support.StandardServletMultipartResolver#745f40ac]
Unable to locate LocaleResolver with name 'localeResolver': using default [org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver#219fc57d]
Unable to locate ThemeResolver with name 'themeResolver': using default [org.springframework.web.servlet.theme.FixedThemeResolver#7b4bd6bd]
Unable to locate RequestToViewNameTranslator with name 'viewNameTranslator': using default [org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator#71ccfa36]
Unable to locate FlashMapManager with name 'flashMapManager': using default [org.springframework.web.servlet.support.SessionFlashMapManager#43f3e6a9]
Published WebApplicationContext of servlet 'dispatcherServlet' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcherServlet]
FrameworkServlet 'dispatcherServlet': initialization completed in 37 ms
I hope I can set it's loadonstartup by 1, and don't want to use this
annoying BeanNameUrlHandlerMapping, it rejected everything and I'm not going to use it.
o.s.w.s.h.BeanNameUrlHandlerMapping : Rejected bean name 'contextAttributes': no URL paths identified
I read the java-doc about BeanNameUrlHandlerMapping:
This is the default implementation used by the org.springframework.web.servlet.DispatcherServlet, along with org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping (on Java 5 and higher). Alternatively, SimpleUrlHandlerMapping allows for customizing a handler mapping declaratively.
That's all, I just want to change these two thing:
setLoadonStartup
don't use BeanNameUrlHandlerMapping
Beside that, other thing spring boot configure for me is very great, and I want to keep it.
Thank you for any help you can provide.
New reply to old post. Seems this is easier to do with more recent versions of Spring Boot. Just adding the property spring.mvc.servlet.load-on-startup=1 works for me.
I encountered the same problem with loadOnStartup. I solved it by using a custom BeanFactoryPostProcessor to modify the BeanDefinition of the ServletRegistrationBean that Spring Boot creates for registering the DispatcherServlet.
The following code will set loadOnStartup for the DispatcherServlet in a Spring Boot app, when used within an #Configuration class:
#Bean
public static BeanFactoryPostProcessor beanFactoryPostProcessor() {
return new BeanFactoryPostProcessor() {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bean = beanFactory.getBeanDefinition(
DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
bean.getPropertyValues().add("loadOnStartup", 1);
}
};
}
BTW, the BeanNameUrlHandlerMapping is harmless here.
It is used to map a Spring Bean to a URL - for example it might be used to support Spring HttpInvoker remoting.
The rejection lines in the log output simply mean that it doesn't recognize any of the Spring beans as beans that require a URL mapping. Annoying messages but harmless. You could always set the logging level for this bean or its package to INFO or above to remove the message. In Spring Boot's application.properties put
logging.level.org.springframework.web.servlet.handler=INFO

No mapping found for HTTP request with URI [/] in DispatcherServlet with name 'dispatcherServlet' but several times [duplicate]

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.

Categories

Resources