Deployment of resources on embedded Tomcat server using Spring Boot - spring

I have a project where data from several sources are being processed into some data structures. After the program is done building these structures, I want it to set up a server that enables users to fine-tune these structures manually. I decided that Spring MVC on an embedded Tomcat server set up using Spring Boot is just what I need.
I want to use Thymeleaf as view technology, and therefore did
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Main {
public static void main(String... args) throws Exception {
// Lots of initialization ...
SpringApplication.run(Main.class, args);
}
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/resources/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
return engine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
viewResolver.setViewNames(new String[]{"*"});
viewResolver.setCache(false);
return viewResolver;
}
}
and
#Controller
public class WebController {
#RequestMapping(value="/greeting", method=RequestMethod.GET)
public String greeting() {
return "greeting";
}
}
But even though there is a view file at /resources/views/greeting.html, the server's reply to the URL http://localhost:8080/greeting is
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "greeting", template might not exist or might not be accessible by any of the configured Template Resolvers
After stepping through the code in a debugger it appears that at some point, ServletContext, which is supposed to return the view file as a stream, looks in a temporary folder like
C:\Users\Michael\AppData\Local\Temp\tomcat-docbase.971027024999448548.8080
which is empty.
Now I get that I need to either
Have the resources deployed to the temporary folder when the server starts up
Have the server operate in the directory where the resources already are
My problem just is that I don't know how to do either, or which approach is the best. Something tells me that 1 is the better wisdom, but any suggestion is welcome.
Edit
Ok, I ended up with something that seems to work. While Joe's answer definitely helped me get on the way, it also appears that I had to change my Maven configuration in a way that puzzles me.
After putting the template greeting.html into /resources/templates/greeting.html and adding resources to the build path, I got the error
javax.servlet.ServletException: Circular view path [greeting]: would dispatch back to the current handler URL [/word/greeting] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
In other words, Thymeleaf seemed to not be properly configured. After some fiddling I ended up changing the version of spring-boot-starter-parent in pom.xml from 0.5.0.BUILD-SNAPSHOT to 0.5.0.M6:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--<version>0.5.0.BUILD-SNAPSHOT</version>-->
<version>0.5.0.M6</version>
</parent>
and removing the version tag from the Thymeleaf dependency
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
<!--<version>${thymeleaf-spring3-version}</version>-->
</dependency>
</dependencies>
After this, it worked.
Can someone please explain why I needed to change the version of spring-boot-starter-parent to be able to remove the version tag from thymeleaf-spring3, and why that was necessary?

The servlet context root is not the best place for templates in an embedded server. There is a way to do it, but if I were you I would go with the flow and use the classpath. If you allow Spring Boot to configure the template resolver (also recommended) then it will look in classpath:/templates by default. There are several samples that use thymeleaf in the Boot code base, so it should be easy to modify one of those if you have different requirements.

Related

Spring boot jackson non_null property not working

I am using Spring boot 1.5.7-RELEASE.
I am trying to set global non null Jackson property in my application.
but it is not working.
I have tried both in application.properties and bootstrap.properties but not working.
spring.jackson.default-property-inclusion=NON_NULL
spring.jackson.serialization-inclusion=NON_NULL
but when I applied on class level, it is working fine.
#JsonInclude(JsonInclude.NON_NULL)
According to the documentation the correct answer is:
spring.jackson.default-property-inclusion=non_null
(note the lowercase non_null - this may be the cause of your problem)
Edit:
I've created a simple Spring Boot 1.5.7.RELEASE project with only the following two compile dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
Then I added the following controller and response classes (using Lombok to skip some boilerplate code):
#RestController
#RequestMapping("/jackson")
public class JacksonTestController {
#GetMapping("/test")
public Response test() {
val response = new Response();
response.setField1("");
return response;
}
}
#Data
class Response {
private String field1;
private String field2;
private Integer field3;
}
Finally I configured Jackson as per documentation, run the application and navigated to http://localhost:8080/jackson/test. The result was (as expected):
{"field1":""}
After that I dug into Spring Boot's source code and discovered that Spring uses class org.springframework.http.converter.json.Jackson2ObjectMapperBuilder to create instances of com.fasterxml.jackson.databind.ObjectMapper. I then put a breakpoint in method public <T extends ObjectMapper> T build() of aforementioned builder class and run my application in debug mode.
I discovered that there are 8 instances of ObjectMapper created during application startup and only one of them is configured using contents of application.properties file. The OP never specified how exactly he was using the serialization, so it's possible his code referred to one of the other 7 object mappers available.
At any rate, the only way to ensure that all object mappers in the application are configured to serialize only non-null properties is to create one's own copy of class org.springframework.http.converter.json.Jackson2ObjectMapperBuilder and etiher hard code that option as default or customize the class to read application.properties during every call to it's constructor or build method.
Maybe I'm late to the party but It may help someone.
Extend WebMvcConfigurationSupport class and customize the Springboot configuration the way you want.
#Configuration
public class Config extends WebMvcConfigurationSupport{
#Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(mapper);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
I was just dealing with the settings in application.properties not taking either. In my case, there was an abstract config class I was extending which defined an ObjectMapper bean that had totally different settings. So I had to override it.
What brought me to the place of finding that was using the /beans Actuator endpoint that Spring Boot apps have, and searching for 'ObjectMapper'. It revealed an instance I didn't realize was being created.

Springboot - Thymeleaf tries to resolve files it should not

Hi I am currently working on a web project that uses thymeleaf and also JSF (its a legacy system and we can only slowly migrate to thymeleaf thats why JSF is still there and cannot be removed from one day to another since this is a lot of work). Thymeleaf is configured to resolve the views in the webapp directory that lie under the directory "thymeleaf". This works perfectly if I deploy the application directly on a tomcat server. Also pages from other directories then the "thymeleaf" directory are also resolved by the JSF framework.
I added some integration tests in JUnit that are using SpringBoot. Inside these tests I got the problem that thymeleaf now is trying to resolve any page in any directory. JSF is completely ignored and I got a whole bunch of JUnit tests failing because of that. Is there any point why thymeleaf ignores its configuration and wants to resolve all files?
Here is my complete thymeleaf configuration, and as I said this works perfectly if I deploy it on a standalone tomcat.
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
String imagesPattern = "/images/**";
String imagesLocation = basePath() + "resources/images/";
registry.addResourceHandler(imagesPattern).addResourceLocations(imagesLocation);
log.info("added resourceHandler (pathPattern: '{}'), (resourceLocation: '{}')",
imagesPattern,
imagesLocation);
String cssPattern = "/css/**";
String cssLocation = basePath() + "resources/css/";
registry.addResourceHandler(cssPattern).addResourceLocations(cssLocation);
log.info("added resourceHandler (pathPattern: '{}'), (resourceLocation: '{}')", cssPattern, cssLocation);
}
#Bean(name = "basepath")
public String basePath()
{
String basepath = "";
File file = new File(Optional.ofNullable(System.getenv("THYMELEAF_APP_RESOURCES"))
.orElse("thymeleaf-resources/"));
if (file.exists())
{
basepath = "file:" + file.getAbsolutePath();
}
if (!basepath.endsWith("/"))
{
basepath += "/";
}
log.info("basepath: {}", basepath);
return basepath;
}
#Bean
#Description("Thymeleaf View Resolver")
public ThymeleafViewResolver viewResolver(String basePath)
{
log.info("setting up Thymeleaf view resolver");
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine(basePath));
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setCache(true);
return viewResolver;
}
public SpringTemplateEngine templateEngine(String basePath)
{
log.info("setting up Thymeleaf template engine.");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver(basePath));
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
private ITemplateResolver templateResolver(String basePath)
{
log.info("setting up Thymeleaf template resolver");
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix(basePath + "thymeleaf/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
return resolver;
}
#Bean
public IMessageResolver thymeleafMessageSource(MessageSource messageSource)
{
SpringMessageResolver springMessageResolver = new SpringMessageResolver();
springMessageResolver.setMessageSource(messageSource);
return springMessageResolver;
}
EDIT
I just found that the problem seems to lie much deeper. Having the dependencies of thymeleaf added into my pom.xml seems to be enough for spring boot to load it into the context... I just deleted my ThymeleafConfig class for testing purposes and still thymeleaf tries to resolve the JSF pages... (yes I did maven clean before executing the test)
EDIT 2
I read it now and tried to exclude the ThymeleafAutoConfiguration class but it does not help. My configurations are still overridden. Here is my configuration for this so far. (And yes this is the ONLY EnableAutoConfiguration annotation in the whole project)
#Configuration
#EnableAutoConfiguration(exclude = {ThymeleafAutoConfiguration.class})
#Import({WebAppConfig.class, ThymeleafConfig.class})
public class SpringBootInitializer extends SpringBootServletInitializer
and my ThymeleafConfig class is already added above.
Having the dependencies of thymeleaf added into my pom.xml seems to be enough for spring boot to load it into the context...
If this has surprised you then I would recommend spending some time to take a step back and read about how Spring Boot works and, in particular, it's auto-configuration feature. This section of the reference documentation is a good place to start.
In short, Spring Boot adopts a convention over configuration approach. If a dependency is on the classpath, Spring Boot assumes that you want to use it, and configures it with sensible defaults. This is what it's doing with Thymeleaf. You can disable this auto-configuration for a specific dependency using the excludes attribute on #SpringBootApplication:
#SpringBootApplication(exclude={ThymeleafAutoConfiguration.class})
public class ExampleApplication {
}
You can also use the spring.autoconfigure.exclude property to provide a comma-separated list of auto-configuration classes to exclude. Each entry in the list should be the fully-qualified name of an auto-configuration class. You could use this property with #TestPropertySource to disable auto-configuration on a test-by-test basis.
I have been struggling with a similar issue for hours and finally found out the root cause.
If you have a dependency to *-data-rest in your pom like this:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
you will have to add Thymeleaf to your pom as well even if you use a another template engine (Freemarker, JSP, ...) everywhere else.
Reason: to expose a JpaRepository as a rest service Spring Boot requires Thymeleaf. I do not understand why this is not defined as a dependency of spring-boot-starter-data-rest so that Maven resolves it automatically.
In my opinion it is a Spring Boot configuration bug.

Spring Boot urls with .html ending

I have very simple hello world created with spring boot (just starter web, no thymeleaf etc.).
I want to handle /hello url and it should produces view from /static/view.html
I have /static/view.html and simple method in my controller:
#RequestMapping("/hello")
public String hello2() {
return "hello.html";
}
The problem is that it causes error:
Circular view path [hello.html]: would dispatch back to the current
handler URL [/hello.html] again
I figured out that it does not matter if I visit /hello or /hello.html, Spring treats them the same.
How can I return simple, static html with the same name as the url path and which object in spring mvc/boot causes mapping url like /example.html to just /example?
You have to perform next steps:
Introduce MVC configuration:
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Put your hello.html into /src/main/webapp/WEB-INF folder
Make sure you have compile dependencies like this:
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.apache.tomcat.embed:tomcat-embed-jasper")
I posted gradle code, if you have maven use similar xml.
NOTE Step 3 is the most important step because if you're using spring boot it applies auto-configurations depending on what you have in classpath. For instance if you add thymeleaf dependency
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
that will most likelly break the code because thymeleaf will introduce its own auto-configured view resolver.
Delete the method in your controller. A #RequestMapping is not needed for static content.

Could not resolve view with name 'htmlviews/index.html' in servlet with name 'dispatcher' using javaconfig

I get such exception:
javax.servlet.ServletException: Could not resolve view with name 'htmlviews/index.html' in servlet with name 'dispatcher'
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1211)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1011)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
when I try to connect to fully java configured spring web service.
My configuration classes:
#Configuration
#EnableWebMvc
#ComponentScan({"config", "controller"})
public class MyWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/htmlviews/**").addResourceLocations("/htmlviews/");
}
}
Initializer:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{MyWebConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return null;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
and controller:
#Controller
public class IndexController {
#RequestMapping(value = "/")
public String getIndexPage() {
return "htmlviews/index.html";
}
#RequestMapping(value = "/{[path:[^\\.]*}")
public String index() {
return "forward:/";
}
}
whole file srtucture is simple :
I am using Idea IDE (also tried in eclipse, same exception) and trying to deploy on tomcat. In pom.xml, I added 'jstl' dependency, but that did not help to resolve problem.
Using xml configuration everything works well. I have no idea what is wrong with my spring java configuration, it is super simple, maybe I forgot something?
Fixed it
Everything started working when I changed spring version from 4.1.0.RELEASE to 4.2.3.RELEASE . I do not why it does not work with 4.1.0.RELEASE. Maybe someone can explain, just curious.
Problem
Spring is trying to find views under your webapp directory. Since you do not have any view resolver, Spring cannot resolve "htmlviews/index.html". In other words, Spring does not know what it is.
You have a Resource Resolver for your html page, which is OK because HTML is static.
Possible Solution 1
In your MyWebConfig class, add the following:
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/htmlviews/", ".jsp");
}
OR you can do this:
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver resolver= new InternalResourceViewResolver();
resolver.setPrefix("/htmlviews/");
resolver.setSuffix(".jsp");
return resolver;
}
Change your html page to jsp page, I recommend that because jsp is simply more powerful than HTML.
Possible Solution 2
Pult all your htmlviews folder under resources so that Spring can find it according to your Resource Resolver.
Update
It's rarely the case that HTML is needed in a Spring boot app. I highly recommend using a template engine (Thymeleaf is preferred). This way, the sensible default setup is sufficient for most of the multi-page applications.
I was trying to implement demo https://spring.io/guides/gs/securing-web/ but i was facing similar problem, To note- this demo only have html with thymleaf (no JSP) and I missed to add thymleaf dependency(reason of error) earlier it showed error
Circular view path []: would dispatch back to the current handler URL ..error
Then I added bean view resolver and it started to give error .
Could not resolve view with name..error
Finally it worked after removing the bean view resolver and adding dependency for thymleaf. Adding this made my project work.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Some more help I found to understand all this working at How to avoid the "Circular view path" exception with Spring MVC test

Spring Boot not serving static content

I can't get my Spring-boot project to serve static content.
I've placed a folder named static under src/main/resources. Inside it I have a folder named images. When I package the app and run it, it can't find the images I have put on that folder.
I've tried to put the static files in public, resources and META-INF/resources but nothing works.
If I jar -tvf app.jar I can see that the files are inside the jar on the right folder:
/static/images/head.png for example, but calling: http://localhost:8080/images/head.png, all I get is a 404
Any ideas why spring-boot is not finding this? (I'm using 1.1.4 BTW)
Not to raise the dead after more than a year, but all the previous answers miss some crucial points:
#EnableWebMvc on your class will disable org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration. That's fine if you want complete control but otherwise, it's a problem.
There's no need to write any code to add another location for static resources in addition to what is already provided. Looking at org.springframework.boot.autoconfigure.web.ResourceProperties from v1.3.0.RELEASE, I see a field staticLocations that can be configured in the application.properties. Here's a snippet from the source:
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
*/
private String[] staticLocations = RESOURCE_LOCATIONS;
As mentioned before, the request URL will be resolved relative to these locations. Thus src/main/resources/static/index.html will be served when the request URL is /index.html. The class that is responsible for resolving the path, as of Spring 4.1, is org.springframework.web.servlet.resource.PathResourceResolver.
Suffix pattern matching is enabled by default which means for a request URL /index.html, Spring is going to look for handlers corresponding to /index.html. This is an issue if the intention is to serve static content. To disable that, extend WebMvcConfigurerAdapter (but don't use #EnableWebMvc) and override configurePathMatch as shown below:
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
super.configurePathMatch(configurer);
configurer.setUseSuffixPatternMatch(false);
}
IMHO, the only way to have fewer bugs in your code is not to write code whenever possible. Use what is already provided, even if that takes some research, the return is worth it.
Edit July 2021:
WebMvcConfigurerAdapter has been deprecated since Spring 5. Implement WebMvcConfigurer and annotate with #Configuration.
Unlike what the spring-boot states, to get my spring-boot jar to serve the content:
I had to add specifically register my src/main/resources/static content through this config class:
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
}
I had a similar problem, and it turned out that the simple solution was to have my configuration class extend WebMvcAutoConfiguration:
#Configuration
#EnableWebMvc
#ComponentScan
public class ServerConfiguration extends WebMvcAutoConfiguration{
}
I didn't need any other code to allow my static content to be served, however, I did put a directory called public under src/main/webapp and configured maven to point to src/main/webapp as a resource directory. This means that public is copied into target/classes, and is therefore on the classpath at runtime for spring-boot/tomcat to find.
Look for Controllers mapped to "/" or with no path mapped.
I had a problem like this, getting 405 errors, and banged my head hard for days. The problem turned out to be a #RestController annotated controller that I had forgot to annotate with a #RequestMapping annotation. I guess this mapped path defaulted to "/" and blocked the static content resource mapping.
The configuration could be made as follows:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcAutoConfigurationAdapter {
// specific project configuration
}
Important here is that your WebMvcConfig may override addResourceHandlers method and therefore you need to explicitly invoke super.addResourceHandlers(registry) (it is true that if you are satisfied with the default resource locations you don't need to override any method).
Another thing that needs to be commented here is that those default resource locations (/static, /public, /resources and /META-INF/resources) will be registered only if there isn't already a resource handler mapped to /**.
From this moment on, if you have an image on src/main/resources/static/images named image.jpg for instance, you can access it using the following URL: http://localhost:8080/images/image.jpg (being the server started on port 8080 and application deployed to root context).
I was having this exact problem, then realized that I had defined in my application.properties:
spring.resources.static-locations=file:/var/www/static
Which was overriding everything else I had tried. In my case, I wanted to keep both, so I just kept the property and added:
spring.resources.static-locations=file:/var/www/static,classpath:static
Which served files from src/main/resources/static as localhost:{port}/file.html.
None of the above worked for me because nobody mentioned this little property that could have easily been copied from online to serve a different purpose ;)
Hope it helps! Figured it would fit well in this long post of answers for people with this problem.
Did you check the Spring Boot reference docs?
By default Spring Boot will serve static content from a folder called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext.
You can also compare your project with the guide Serving Web Content with Spring MVC, or check out the source code of the spring-boot-sample-web-ui project.
I think the previous answers address the topic very well. However, I'd add that in one case when you have Spring Security enabled in your application, you might have to specifically tell Spring to permit requests to other static resource directories like for example "/static/fonts".
In my case I had "/static/css", "/static/js", "/static/images" permited by default , but /static/fonts/** was blocked by my Spring Security implementation.
Below is an example of how I fixed this.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.....
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/fonts/**").permitAll().
//other security configuration rules
}
.....
}
This solution works for me:
First, put a resources folder under webapp/WEB-INF, as follow structure
-- src
-- main
-- webapp
-- WEB-INF
-- resources
-- css
-- image
-- js
-- ...
Second, in spring config file
#Configuration
#EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resource/**").addResourceLocations("WEB-INF/resources/");
}
}
Then, you can access your resource content, such as
http://localhost:8080/resource/image/yourimage.jpg
Just to add yet another answer to an old question... People have mentioned the #EnableWebMvc will prevent WebMvcAutoConfiguration from loading, which is the code responsible for creating the static resource handlers. There are other conditions that will prevent WebMvcAutoConfiguration from loading as well. Clearest way to see this is to look at the source:
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java#L139-L141
In my case, I was including a library that had a class that was extending from WebMvcConfigurationSupport which is a condition that will prevent the autoconfiguration:
#ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
It's important to never extend from WebMvcConfigurationSupport. Instead, extend from WebMvcConfigurerAdapter.
UPDATE: The proper way to do this in 5.x is to implement WebMvcConfigurer
Put static resources under the directory:
/src/main/resources/static
add this property in application.properties file
server.servlet.context-path=/pdx
You can access from http://localhost:8080/pdx/images/image.jpg
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("/")
public String home(Map<String, Object> model) {
model.put("message", this.message);
return "index";
}
}
index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>index</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'm using Spring Boot 2.2 and not getting any of my static content. I discovered two solutions that worked for me:
Option #1 - Stop using #EnableWebMvc annotation
This annotation disables some automatic configuration, including the part that automatically serves static content from commonly-used locations like /src/main/resources/static. If you don't really need #EnableWebMvc, then just remove it from your #Configuration class.
Option #2 - Implement WebMvcConfigurer in your #EnableWebMvc annotated class and implementaddResourceHandlers()
Do something like this:
#EnableWebMvc
#Configuration
public class SpringMVCConfiguration implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/js/");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/css/");
registry.addResourceHandler("/vendor/**").addResourceLocations("classpath:/static/vendor/");
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
}
Just remember that your code is now in charge of managing all static resource paths.
In case the issue surfaces when launching the application from within an IDE (i.e. starting from Eclipse or IntelliJ Idea), and using Maven, the key to the solution is in the Spring-boot Getting Started documentation:
If you are using Maven, execute:
mvn package && java -jar target/gs-spring-boot-0.1.0.jar
The important part of which is adding the package goal to be run before the application is actually started. (Idea: Run menu, Edit Configrations..., Add, and there select Run Maven Goal, and specify the package goal in the field)
I was facing the same issue in spring boot 2.1.3 saying that resource not found 404. I removed below from applicatiion.properties.
#spring.resources.add-mappings=true
#spring.resources.static-locations=classpath:static
#spring.mvc.static-path-pattern=/**,
Removed #enableWebMVC and removed any WebMvcConfigurer overriding
//#EnableWebMvc
Also make sure you have #EnableAutoConfiguration in your config.
And put all static resources into src/main/resources/static and it just worked like magic finally..
I am using 1.3.5 and host a bunch of REST-services via Jersey implementation. That worked fine until I decided to add a couple of HTMLs + js files.
None of answers given on this forum helped me. However, when I added following dependency in my pom.xml all the content in src/main/resources/static was finally showing via browser:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<dependency>
It seems spring-web / spring-webmvc is the important transitive dependency that makes spring boot auto config turn on.
FYI: I also noticed I can mess up a perfectly working spring boot app and prevent it from serving contents from the static folder, if I add a bad rest controller like so
#RestController
public class BadController {
#RequestMapping(method= RequestMethod.POST)
public String someMethod(#RequestParam(value="date", required=false)String dateString, Model model){
return "foo";
}
}
In this example, after adding the bad controller to the project, when the browser asks for a file available in static folder, the error response is '405 Method Not Allowed'.
Notice paths are not mapped in the bad controller example.
using spring boot 2.*, i have a controller that maps to routes GetMapping({"/{var}", "/{var1}/{var2}", "/{var1}/{var2}/{var3}"}) and boom my app stop serving resources.
i know it is not advisable to have such routes but it all depends on the app you are building (in my case, i have no choice but to have such routes)
so here is my hack to make sure my app serve resources again. I simply have a controller that maps to my resources. since spring will match a direct route first before any that has variable, i decided to add a controller method that maps to /imgaes/{name} and repeated same for other resources
#GetMapping(value = "/images/{image}", produces = {MediaType.IMAGE_GIF_VALUE, MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
public #ResponseBody
byte[] getImage(#PathVariable String image) {
ClassPathResource file = new ClassPathResource("static/images/" + image);
byte[] bytes;
try {
bytes = StreamUtils.copyToByteArray(file.getInputStream());
} catch (IOException e) {
throw new ResourceNotFoundException("file not found: " + image);
}
return bytes;
}
and this solved my issue
Requests to /** are evaluated to static locations configured in
resourceProperties.
adding the following on application.properties, might be the only thing you need to do...
spring.resources.static-locations=classpath:/myresources/
this will overwrite default static locations, wich is:
ResourceProperties.CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
You might not want to do that and just make sure your resources end up in one of those default folders.
Performing a request:
If I would have example.html stored on /public/example.html
Then I can acces it like this:
<host>/<context-path?if you have one>/example.html
If I would want another uri like <host>/<context-path>/magico/* for files in classpath:/magicofiles/* you need a bit more config
#Configuration
class MyConfigClass implements WebMvcConfigurer
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/magico/**").addResourceLocations("/magicofiles/");
}
In my case I have a spring boot application which is kind of mixing spring and jaxrs. So I have a java class which inherits from the class org.glassfish.jersey.server.ResourceConfig. I had to add this line to the constructor of that class so that the spring endpoints are still called: property(ServletProperties.FILTER_FORWARD_ON_404, true).
Had the same problem, using gradle and eclipse and spent hours trying to figure it out.
No coding required, the trick is that you must use the menu option New->Source Folder (NOT New -> Folder) to create the static folder under src/main/resources. Don't know why this works, but did new -> source folder then i named the folder static (then source folder dialog gives an error for which you must check: Update exclusion filters in other source folders to solve nesting). The my new static folder I added index.html and now it works.
Well sometimes is worth to check did you override the global mappings by some rest controller. Simple example mistake (kotlin):
#RestController("/foo")
class TrainingController {
#PostMapping
fun bazz(#RequestBody newBody: CommandDto): CommandDto = return commandDto
}
In the above case you will get when you request for static resources:
{
title: "Method Not Allowed",
status: 405,
detail: "Request method 'GET' not supported",
path: "/index.html"
}
The reason for it could be that you wanted to map #PostMapping to /foo but forget about #RequestMapping annotation on the #RestController level. In this case all request are mapped to POST and you won't receive static content in this case.
Given resources under src/main/resources/static,
if you add this code, then all static content from src/main/resources/static will be available under "/":
#Configuration
public class StaticResourcesConfigurer implements WebMvcConfigurer {
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/resources/static/");
}
}
In my case, some static files were not served, like .woff fonts and some images. But css and js worked just fine.
Update: A much better solution to make Spring Boot serve the woff fonts correctly is to configure the resource filtering mentioned in this answer, for example (note that you need both includes and excludes):
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>static/aui/fonts/**</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>static/aui/fonts/**</include>
</includes>
</resource>
</resources>
----- Old solution (working but will corrupt some fonts) -----
Another solution was to disable suffix pattern matching with setUseSuffixPatternMatch(false)
#Configuration
public class StaticResourceConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// disable suffix matching to serve .woff, images, etc.
configurer.setUseSuffixPatternMatch(false);
}
}
Credits: #Abhiji did point me with 4. in the right direction!
Works for Thymeleaf, you can link the stylesheet using
<link th:href="#{/css/style.css}" rel="stylesheet" />
As said above, the file should be in $ClassPath/static/images/name.png, (/static or /public or /resources or /META-INF/resources). This $ClassPath means main/resources or main/java dir.
If your files are not in standard dirs, you can add the following configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/lib/**"); // like this
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// ... etc.
}
...
}

Resources