Spring Boot project with static content generates 404 when running jar - spring

The recent blog post (https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot) by Spring regarding the use of static web content in Spring Boot projects indicates that several resource directories may be used:
/META-INF/resources/
/resources/
/static/
/public/
This is thanks to the WebMvcAutoConfiguration class which automatically adds these directories to the classpath. This all seems fine and appears to work when using the spring-boot-maven-plugin spring-boot:run goal, all of your static content is working (eg: /index.html).
When you package your Spring Boot project and allow the spring-boot-maven-plugin to create the enhanced JAR then attempt to run your project using java -jar my-spring-boot-project.jar you find that your static content now returns a 404 error.

It turns out that whilst Spring Boot is being clever at adding the various resource directories to the classpath, Maven is not and it's up to you to deal with that part. By default, only src/main/resources will be included in your JAR. If you create a folder called /static in the root of your project (as implied by the blog post) then it will work fine whilst using the spring-boot:run Maven goal but not once you create a JAR.
The easiest solution is to create your /static folder inside /src/main/resources and then Maven will include it in the JAR. Alternative you can add additional resource locations to your Maven project:
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>static</directory>
<targetPath>static</targetPath>
</resource>
</resources>
I hope this is useful to someone, it's kind of obvious when you step back and look at how Maven works but it might stump a few people using Spring Boot as it's designed to be pretty much configuration free.

I am banging my head against the wall trying to figure out how to do this with gradle. Any tips?
EDIT: I got it to work by adding this to my build.gradle:
// Copy resources into the jar as static content, where Spring expects it.
jar.into('static') {
from('src/main/webapp')
}

There are 2 things to consider (Spring Boot v1.5.2.RELEASE)-
1) Check all Controller classes for #EnableWebMvc annotation, remove
it if there is any
2) Check the Controller classes for which annotation is used -
#RestController or #Controller.
Do not mix Rest API and MVC behaviour in one class. For MVC use #Controller and for REST API use #RestController
Doing above 2 things resolved my issue. Now my spring boot is loading static resources with out any issues.
#Controller => load index.html => loads static files.
#Controller
public class WelcomeController {
// inject via application.properties
#Value("${welcome.message:Hello}")
private String message = "Hello World";
#RequestMapping("/welcome")
public String welcome(Map<String, Object> model) {
model.put("message", this.message);
return "welcome";
}
#RequestMapping("/")
public String home(Map<String, Object> model) {
model.put("message", this.message);
return "index";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet/less" th:href="#{/webapp/assets/theme.siberia.less}"/>
<!-- The app's logic -->
<script type="text/javascript" data-main="/webapp/app" th:src="#{/webapp/libs/require.js}"></script>
<script type="text/javascript">
require.config({
paths: { text:"/webapp/libs/text" }
});
</script>
<!-- Development only -->
<script type="text/javascript" th:src="#{/webapp/libs/less.min.js}"></script>
</head>
<body>
</body>
</html>

I was going around few pages to understand how to serve static content in Spring boot environment. Mostly all advises was around placing the static files with in /static /resources/ src/main/webapp etc. Thought of sharing below approach.
Allow spring boot to auto configure Dispatcher Servlet - Make sure DispatcherServletAutoConfiguration is not in the exclude for AutoConfiguration.
#EnableAutoConfiguration(exclude =
{
//DispatcherServletAutoConfiguration.class,
})
Inject your external directory for static content routing
#Value("${static-content.locations:file:C:/myprj/static/")
private String[] staticContentLocations;
3.Override WebMvcAutoConfiguration using WebMvcConfigurerAdapter to advice spring not to use default resource Location but use what we instruct it.Like below
#Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter()
{
return new WebMvcConfigurerAdapter()
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
if (!registry.hasMappingForPattern("/**"))
{
// if this is executed spring won't add default resource
// locations - add them to the staticContentLocations if
// you want to keep them
// default locations:
// WebMvcAutoConfiguration.RESOURCE_LOCATIONS
registry.addResourceHandler("/**").addResourceLocations(
staticContentLocations);
}
}
};
}
If C:/myprj/static has index.html , then http://localhost:portno/index.html should work. Hope that helps.

Spring Boot will use WebMvcAutoConfiguration to config static contents, this class will only take effects when you don't have a custom WebMvcConfigurationSupport bean ,and in my cases , i do have one.
#Configuration
#ConditionalOnWebApplication
#ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
#ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
#AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
#AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}
so i'll just have to config it by myself, here is my solution:
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static-file/**").addResourceLocations("classpath:/static/");
}

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>

I've a assets folder in static dir and permitting in SpringConfig which extended WebSecurityConfigurerAdapter.
http.authorizeRequests().antMatchers("/", "/login", "/assets/**")
.permitAll()

I just stumbled about this 404 topic when I try to create a sandbox example as template (https://github.com/fluentcodes/sandbox/tree/java-spring-boot) from my existing working solution: No static content index.html is provided.
Since the example was rather simple and the existing solution worked I don't get deep into the deep digging spring solutions mentioned.
I looked in the created target classes and there was no static/index.html as expected.
The reason was rather simple: I created the resources not under src/main/ but under src/. Need some time to find since there are so many complex solutions mentioned. For me it had a rather trivial reason.
On the other hand when starting spring boot: Why I don't get any hint, that no static content from location1, location2 .... locationx could be found. Think its a topic.

Related

Spring MVC + Thymeleaf: not loading some resources

I am new to Thymeleaf and Spring MVC.
I have been dealing with the following problem: some resources (css or images) don't get loaded by my webpage while other do. They are in the same path and folder, the syntax is the same (i have checked by just switching the name of the resource and it worked).
For example, my Thymeleaf Template can find and read my own css files, but it won't read the bootstrap-4 one.
Here is my project structure:
And here an example of the code trying to read bootstrap.css:
The same problem happens with images of the same format.
Any ideas of what could be causing the issue?
Thank you in advance
You can register a resource handler by extending a WebMvcConfigurerAdapter.
Something like this.
#EnableWebMvc
#Configuration
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/styles/**").addResourceLocations("/styles/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
Anyway, the css files should be in the css directory
Okay so I understood what the problem was: intelliJ.
It didn't see the new files added to the project.
Running mvn clean install fixed the problem

Spring MVC Cannot configure views' path

I have a project that uses Spring Boot 2.2.5 (Spring version 5) - here is a link to a bare minimum project demonstrating my problem. In all the tutorials I followed they claim views' path can either be configured inside application.properties like this:
spring.mvc.view.prefix=/WEB-INF/jsp
spring.mvc.view.suffix=.jsp
or inside WebMvcConfigurationSupport derived class like this:
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("classpath:/", ".jsp");
}
or like this:
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
registry.viewResolver(resolver);
}
None of which work in my case. Spring will always serve .jsp files from src/main/webapp and from nowhere else in spite of my configuration or lack of it. No other file types will be served from that directory, not even HTML.
Some tutorials claim that when not configured Spring will serve anywhere from
src/main/resources/static
src/main/resources/public
src/main/resources/resources
src/main/java/META-INF/resources
I am yet to see this.
CSS and Javascript files will be served from src/main/resources but only if I have this in my MVC configuration:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/");
}
Configuring this from application.properties doesn't work
spring.resources.static-locations=classpath:/
In relation to this are there other special folder names like classpath: that can be used? I tried webapp: but it doesn't seem to be expanded
UPDATE: I thought for a moment that maybe subclassing WebMvcConfigurationSupport is to blame since it acts like #EnableWebMvc. Subclassing WebMvcConfigurer brought the following error. Placing #EnableWebMvc solves it.
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration.requestMappingHandlerAdapter(WebMvcAutoConfiguration.java:369)
The following method did not exist:
'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration.requestMappingHandlerAdapter(org.springframework.web.accept.ContentNegotiationManager, org.springframework.format.support.FormattingConversionService, org.springframework.validation.Validator)'
I read somewhere that JSP view are not supported inside embedded servlets. What a nice feature! Anyway I don't thing this is related to my problem.
I would like to stick to JSP and avoid Thymeleaf as my project is based on React. I will create MVC pages in order to be Search engine friendly, though but I will figure this out along the way.
Here is a screenshot of my project's layout

Unable to access the html file in template folder of spring boot application

html files are placed under resources/templates/login.html directory of spring boot application(show in the screenshot), deployed it in the weblogic server and when I try to access the login.html with the below URL, it gives The webpage cannot be found message
http://localhost:7001/demo/login.html
below is the screenshot
In one of the post I found the below code snippet and tried, but it didn't work
#Configuration
public class StaticResourceConfiguration extends WebMvcConfigurerAdapter {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
I am not getting what mistake I did, Could some one help me regarding this ...?
Spring Boot by default serves all content found under /static, /public, /resources or /META-INF/resources, see docs. So all content in your static folder should be served well (check that). But the templates folder is not a sub-folder of the static, so it will not be served. If I get you right, the templates is not supposed to be part of the URL path, right? So you could either move your login.html to the static folder, or you could add the templates folder to the classpath resource locations. Either programmatically (as you did for the other locations), or by setting the corresponding property:
spring.resources.static-locations=classpath:/templates/,classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/

Serving static contents in Spring Boot

I am developing a new poc for web application from Spring Boot. The packaging type of my application in war. In this all i want is to display some contents on a jsp. For that i have created a small jsp, and requierd css/images/js files i have put in resources/static folder. So my static folder contains css/images/js folders. I've added following code in my configuration file. My configuration extends from WebMvcConfigurerAdapter
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String[] pathPatterns = {"/components/**", "/images/**", "/scripts/**", "/styles/**"};
String[] resourceLocations = {"classpath:/static/components/", "classpath:/static/images/", "classpath:/static/scripts/, classpath:/static/styles/"};
registry.addResourceHandler(pathPatterns).addResourceLocations(resourceLocations);
}
However, my jsp does not get the reference of these file.
JSP Code
How to solve above problem..
Second concern, as per the spring boot reference documentation, it serves the static content which are located in static folder. that means i should be able to access files from my static folder directly in below way
http://localhost:8080/styles/main.css
But this is also not working
Third Issue - static contents are served by default servlet ..is this true that Default servlet in enabled by default in Spring Boot application
Please Help
Putting the static resources inside src/main/resources/static folder works for me without any addResourceHandlers configuration. For example, I have a css file at
src/main/resources/static/public/css/styles.css
which I refer from my JSP like this:
<link href="/public/css/styles.css" rel="stylesheet">
You should have put your JSPs inside src/main/webapp/WEB-INF, and set the packaging to war rather than jar, due to the limitations of having JSPs in Spring Boot.

Spring MVC cannot find *.jsp files

I am an absolute noob when it comes to web development. But I got some background in C/C++/Java so I don't have a problem with MVC controllers. It's the configuration that is giving me the headache.
I am using Spring Boot. And according to the tutorials it can magically resolve everything without even opening an editor and typing a single character. Apparently not.
I have a view resolver configure as such:
#Configuration
#ComponentScan (basePackages = {"my.test.controller"})
#EnableAutoConfiguration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver getViewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(WebConfig.class, args);
}
}
I have a controller like this:
#Controller
public class PageController {
#RequestMapping(value = "/index")
public String doSomething() {
//.. do Something
return "/index";
}
My main problem is it cannot find the file if there is a jsp extension in the address.
If I type the url without an extension like localhost:8080/index the page is displayed properly.
If I type the url with an extension like localhost:8080/index.jsp the page returns a 404 error. This is the same for all pages declared in the controller.
Any help will be greatly appreciated. Thanks thanks.
There's a JSP sample in Spring Boot that you can crib from. If I were you I wouldn't define a ViewResolver since Boot already does that for you (but if you want to use prefix and suffix resolution you need to set spring.view.prefix and spring.view.suffix).
Your #Controller should return view names (not paths), so "index" is going to be resolved as "/WEB-INF/views/index.jsp" with your existing setup. I also wouldn't bother with the "/resources" mapping since one is already provided by Spring Boot, albeit a different one than you defined (normally people put static resources in "classpath:/static" but "classpath:/resources" works as well and there is no prefix for the path to the resource in the HTTP endpoints).
JSP is inferior to other view technologies in so many ways, so it is unfortunate that it is so ubiquitous. There are many limitations, including restrictions on the way you can package and run a Boot application (see here for details). It would be worth your effort to unlearn JSP if you can spare the time.
I remember having the same problem when I started with spring, the "url" that you use needs to correspond to a particular Request Mapping, not necessarily a particular page
for example
#RequestMapping(value = "/home")
public String doSomething() {
//.. do Something
return "/index";
}
will expose an endpoint at localhost:8080/home not localhost:8080/index or localhost:8080/index.jsp
A great example project is located at:
https://github.com/mariuszs/spring-boot-web-jsp-example
Well, I am not sure this answer will help you, as the question was posted in 2014. In order to help people resolve this question, I provide some of my resolutions. Hope this will help.
make sure your #Controller 's configuration #RequestMapping("/xx") cannot be the same as your view (jsp or templates)
For example, you have a view named home.html. You cannot let the #RequestMapping() be the same as the view's name. This will cause a circular error (--> Circular view path, added below).
How to fix this error, The path cannot be the name. (That's the JSP files mostly happen)
When you input the same name, you will get this:
Circular view path [preference]: would dispatch back to the current handler URL [/preference] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Here is a link that explains why this error would happen.
This kind of error is only for the HTML5 file. When you get some wite page error and you are using the HTML5 file and cannot find other errors, that might be this one below
When you create an HTML file, the basic file will be the below code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
The <meta charset="UTF-8"> didn't end with /> or </meta>.
When in the HTML5 file, that would be right. But Thymeleaf uses XHTML to load the file, so the <meta> should be closed.
If you mapped all of your requests to dispatcher servlet from web.xml,then it will check for the appropriate controller mappings.
You have mapped the request to /index so it can't process a /index.jsp
then internal view resolver will return the view just like you configured.
you can try
#RequestMapping(value = {"/index","/index.jsp"})
It is better to avoid .jsp extension in a web app.

Resources