Spring Boot static content with context path - java

I'm having problems with static content in Spring Boot when using a context path. i.e.: I wish to deploy my app to localhost:8080/{appname}/
When I run the app without context path everything works fine and Spring Boot finds and runs my .html files (from resources/templates/, I'm using Thymeleaf) and JS files (from resources/static/js/) but when I add the context path with either:
server.context-path=/{appname}
OR
server.servlet-path=/{appname}
then the .html pages are still displayed by the JS files generate 404 errors.
I have tried to change the spring.resources.static-locations in the application.properties and overriding addResourceHandlers() method in my MvcConfig.class but neither seem to work
I use a MvcConfig class because I need to define a CookieLocaleResolver and a MessageSource but that is all I have in the MvcConfig. I do not use #EnableWebMvc and just have the #SpringBootApplication annotation with a #ComponentScan.
Any help would be appreciated :)

As per your comment:
the HTML is referencing the JS without the app context
the problem is not in Spring serving the js, its that the page is not creating the URL to the resources correctly.
Thymeleaf provides a mechanism to support this automatically by just marking the src attribute with the th prefix.
See section 2 "Context-relative URLs": www.thymeleaf.org/doc/articles/standardurlsyntax.html

Below is an example shows how to configure static resources in spring boot.
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**/*.js").addResourceLocations("/ui/static/");
registry.addResourceHandler("/**/*.css").addResourceLocations("/ui/static/");
}
}
Path patterns
Add a resource handler for serving static resources based on the specified URL path patterns. The handler will be invoked for every incoming request that matches to one of the specified path patterns.
Patterns like "/static/" or "/css/{filename:\w+\.css}"} are allowed.
See **org.springframework.util.AntPathMatcher for more details on the syntax.
your jsp/html looks refer static content as in below
<link href="/webAppContext/cssa/bootstrap.min.css" rel="stylesheet"/>
<script src="/webAppContext/jquery-2.2.1.min.js"></script>
<script src="/webAppContext/bootstrap.min.js"></script>
urls using which browser tries to get static content
http://localhost:8080/webAppContext/jquery-2.2.1.min.js
http://localhost:8080/webAppContext/bootstrap.min.js
http://localhost:8080/webAppContext/cssa/bootstrap.min.css
server.servlet.context-path=/webAppContext is in your application.properties

My config is:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
public static final Logger LOG = LoggerFactory.getLogger(MvcConfig.class);
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setCookieMaxAge(-1);
cookieLocaleResolver.setDefaultLocale(Locale.FRENCH);
return cookieLocaleResolver;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:i18n/lang");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Urli18nRewriterInterceptor());
}
}
and folder structure:
folder structure

Related

How do you resolve static pages in multiple controllers with different #RequestMapping

I have 2 different controllers annotated with #RequestMapper, one specifying "/" and the other specifying "/rmo".
The controller with "/" properly resolves static resources such as .css and .js files. The resources used by the controller with "/rmo" in the #RequestMapping have /rmo prefixed to the URLs for the static resources. How do I get the /rmo controller to properly resolve the static resources?
I think I found the answer. In my ApplicationConfiguration which extends WebMvcConfigurerAdapter I have the following override:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
if (logger.isDebugEnabled())
logger.debug("addResourceHandlers");
super.addResourceHandlers(registry);
registry.addResourceHandler("/**").addResourceLocations(RESOURCE_LOCATIONS);
}
By adding the following line, my problem is apparently solved:
registry.addResourceHandler("/rmo/**").addResourceLocations(RESOURCE_LOCATIONS);
I guess I'll have to do this for each controller.

Spring: ResourceResolver not called from error page

I have a Spring Boot web application that adds a resource handler for static resources (mainly CSS and JS files) using the addResourceHandlers method of WebMvcConfigurerAdapter. This resource handler is further configured to enable a VersionResourceResolver Basically this is
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
...
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
ResourceHandlerRegistration registration = registry.addResourceHandler(...)
ResourceChainRegistration chainRegistration = registration.resourceChain(...);
chainRegistration.addResolver(
new VersionResourceResolver().addContentVersionStrategy(...));
...
}
}
Now the problem is, when the user sees an error page (for example the 404 page), this VersionResourceResolver won't be used. (The error pages are configured using #ExceptionHandlers in a #ControllerAdvice annotated class.) The error pages will be rendered and displayed fine, however, the resources loaded on this page don't have version applied.
The Spring Boot documentation has a note saying that an ERROR dispatcher needs special treatment, however, I cannot figure out how to apply this advice in the context of our WebMvcConfigurerAdapter.
Any help is appreciated.
I had the same problem in a my opensource project and I resolve in this way.
I create an #Controller class like this:
#Controller
public class ExceptionController {
#Autowired
private MessageSource messageSource;
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
#RequestMapping("/exception")
public String exception(Model model,Exception ex,Locale locale,HttpServletRequest httpRequest,HttpServletResponse httpResponse){
ex.printStackTrace();
model.addAttribute("templatePath", "exception/exception");
model.addAttribute("template", "content");
try{
model.addAttribute("exceptionMessage",messageSource.getMessage(String.format("exception.body.%s",httpResponse.getStatus()),new Object[]{},locale));
} catch (NoSuchMessageException e){
model.addAttribute("exceptionMessage",messageSource.getMessage("exception.body",new Object[]{},locale));
}
return "index";
}
}
and then I customize the servlet container configuration in this way:
#Bean
public EmbeddedServletContainerCustomizer exceptionHandling() {
return container -> container.addErrorPages(new ErrorPage("/exception"));
}
in feew words I make a special page for error /exception in this case and then configure the embedded tomcat for use this url in case of exceptions
In the Autoconfiguration classes of Spring boot you can see in org.springframework.boot.autoconfigure.web.BasicErrorController this documentation:
Basic global error {#link Controller}, rendering {#link ErrorAttributes}. More specific
* errors can be handled either using Spring MVC abstractions (e.g.
* {#code #ExceptionHandler}) or by adding servlet
* {#link AbstractEmbeddedServletContainerFactory#setErrorPages container error pages}
I hope that tis can help you

Thymeleaf: second resolver for SVG in Spring Boot

I'm trying to set up a second resolver for SVGs which use thymeleaf attributes, so it needs to be resolved by the template engine within my spring boot application. My implementation so far:
ThymeleafSvgConfig
#Configuration
public class ThymeleafSvgConfig {
#Bean
public FileTemplateResolver svgTemplateResolver(){
FileTemplateResolver svgTemplateResolver = new FileTemplateResolver();
svgTemplateResolver.setPrefix("classpath:/svgtemplates/");
svgTemplateResolver.setSuffix(".svg");
svgTemplateResolver.setTemplateMode("XML");
svgTemplateResolver.setCharacterEncoding("UTF-8");
svgTemplateResolver.setOrder(0);
return svgTemplateResolver;
}
}
ThymeleafSvgController
#Controller
public class ThymeleafSvgController {
#Autowired
private ServletContext servletContext;
#Autowired
private TemplateEngine templateEngine;
//.. logging etc.
#RequestMapping(value = "/getSvg", method = { RequestMethod.GET, RequestMethod.POST })
public void getSvg(#RequestParam(required = false) String type, HttpServletRequest request, HttpServletResponse response) {
WebContext context = new WebContext(request, response, servletContext, request.getLocale());
templateEngine.process("test", context, response.getWriter());
}
}
I read that Spring Boot searches for resolvers automatically and adds them to the engine [s. this]. So I assumed, that my Config should be enough and that via autowiring I should get the correct template engine.
But still I receive the same error in my application, when trying to call:
templateEngine.process("test", context, response.getWriter());
My SVG is saved in /src/main/resources/svgtemplates/test.svg
Exception:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "test", template might not exist or might not be accessible by any of the configured Template Resolvers
I'm not sure what I misconfigured, so I need some help with this. Is the FileTemplateResolver the correct class for this? I also tried the ClassLoaderTemplateResolver but it threw the same exception. The standard Thymeleaf Resolver from spring boot works fine, which works with the standard prefix: "classpath:/templates/". So considering that 'templates' and 'svgtemplates' lie in the same folder, my prefix should be correct so far, shouldn't it?
Maybe someone else sees the flaw.
The problem was that the FileTemplateResolver can't cope with the 'classpath:' notation. After providing the complete file path, the resolver works.
You can use the SpringResourceTemplateResolver with Spring Boot which does support the classpath: notation:
#Bean
public ITemplateResolver svgTemplateResolver()
{
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/svg");
resolver.setSuffix(".svg");
resolver.setTemplateMode("XML");
return resolver;
}
You may also want to set spring.thymeleaf.template-resolver-order = 0 in you application configuration to prioritise the default resolver (typically used for the HTML templates).

Configure ViewResolver with Spring Boot and annotations gives No mapping found for HTTP request with URI error

I'm trying to make "hello world" application with gradle, spring boot and spring mvc with the simplest view resolver and html.
I started from the thymeleaf spring boot example and I just wanted to remove thymeleaf to make a simpler mvc application using pure html and InternalResourceViewResolver. I have a single greeting.html I want to serve which is located at src/main/webapp/WEB-INF. When I run the app I get
No mapping found for HTTP request with URI [/WEB-INF/greeting.html] in DispatcherServlet with name 'dispatcherServlet'
This is a common error and there are a lot of answers on the web but nothing seems to help.
Here is my Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Here is my GreetingController.java
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
}
Here is my MvcConfiguration.java
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
}
I run it with gradle bootRun
Here is the repo with the code: https://github.com/driver-pete/spring-mvc-example
Here are some more clues:
Thymeleaf view resolving works fine
InternalResourceViewResolver resolves to the right path
WEB-INF and greeting.html seems to be present in the war file
I do not have jsp or jstl so I do not miss those jars as some might suggest
My hypothesis is that dispatcher servlet somehow get configured to serve on /* instead of / like here and everywhere. However I don't have web.xml so those advices do not apply here. I see a lot of examples how to configure dispatcher servlet programmatically but I want to keep my app at minimum and I suspect that spring boot is supposed to configure it ok since it works fine with thymeleaf.
You only need to enable the default servlet, this is done by adding the following to your MvcConfiguration:
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Essentially what is happening is Spring does not know how to handle the handling of such content natively(could be a jsp say), and to this configuration is the way to tell it to delegate it to the container.
View resolver can also be configured in application.properties file of Spring-Boot web applications, something like below:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
After investigating more I discovered an alternative solution that works without adding configureDefaultServletHandling method. You need to add an embedded tomcat jsp engine to build.gradle:
compile("org.apache.tomcat.embed:tomcat-embed-jasper")
As opposed to configureDefaultServletHandling method this solution works not only with plain html but also with jsp.
All solutions are available at: https://github.com/driver-pete/spring-mvc-example
This solution is available on master.
Biju's solution is on DefaultServletHandling_solution branch.
If you are using spring above 5.0, then use org.springframework.web.servlet.view.InternalResourceViewResolver
instead of
org.springframework.web.servlet.InternalResourceViewResolver
in your bean definition

Spring 4 - addResourceHandlers not resolving the static resources

My maven spring project directory structure is shown below. I am using Spring-4 annotation based configuration. I configure the resources like below. I tried many ways that are suggested in many Stackoverflow questions and other websites
Spring 4 loading static resources
http://imwill.com/spring-mvc-4-add-static-resources-by-annotation/#.U5GZlXKs9i4
But the jsp files could not load the resources, all the static content requests returns 404 error. I tried these things in jsp,
<link href="resources/css/bootstrap.css" rel="stylesheet" media="screen">
<link href="/resources/css/bootstrap.css" rel="stylesheet" media="screen">
<link href="css/bootstrap.css" rel="stylesheet" media="screen">
EDIT: I am using servlet 2.5 because as of now I cannot upgrade my project from JBoss 5 to higher versions. JBoss5 do not support servlets 3, and do that matter?
#Configuration
#ComponentScan("com.mgage.mvoice")
public class MyAppWebConfig extends WebMvcConfigurerAdapter {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// I tried these many combinations separately.
ResourceHandlerRegistration resourceRegistration = registry
.addResourceHandler("resources/**");
resourceRegistration.addResourceLocations("/resources/**");
registry.addResourceHandler("/css/**").addResourceLocations("/css/**");
registry.addResourceHandler("/img/**").addResourceLocations("/img/**");
registry.addResourceHandler("/js/**").addResourceLocations("/js/**");
registry.addResourceHandler("/resources/**")
.addResourceLocations("classpath:/resources/");
// do the classpath works with the directory under webapp?
}
}
this worked,
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
and in the jsp files I referred to the static resources like
<link href="resources/css/bootstrap.css" rel="stylesheet" media="screen">
I guess it's a bit late, however I was facing with a similar problem just recently. After several days of wrestling, finally it turned out that my DispatcherServlet was not configured to handle the request, therefore the resources were never looked up. So I hope others will find this answer useful.
If the dispatcher servlet to that you give your config class above is mapped not to the root ("/") but to a top word (e.g. "/data/"), then you might face with the same problem.
Suppose I have a mapping as "/data/*" for my dispatcher servlet. So my calls look like
http://localhost:8080/myWebAppContext/data/command
and I thought that if I have a resource mapping e.g. "/content/**/*", then I have access to it as
http://localhost:8080/myWebAppContent/content/resourcePath
but it's not true, I should use
http://localhost:8080/myWebAppContent/data/content/resourcePath
instead. This was not clear for me, and since most of the samples use the root "/" for the dispatcher servlet's mapping, therefore it was not an issue there. Upon considering later I should have known it earlier - /data/ tells that the DispatcherServlet should evaluate the call, and the content/ tells to the servlet that a resource handler is the "controller".
But I want to make it very clear in my frontend (angularJs) whether I look for data (via the REST services) or a content (returning plain texts). The data comes from a database, but the content comes from files (e.g. pdf docs). Therefore, I decided to add two mappings to the dispatcher servlet:
public class MidtierWebConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(MidtierAppConfig.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(MidtierDispatcherConfig.class);
Dynamic netskolaDispatcher = servletContext.addServlet(
"dispatcher",
new DispatcherServlet(dispatcherContext)
);
netskolaDispatcher.setLoadOnStartup(1);
netskolaDispatcher.addMapping("/data/*");
netskolaDispatcher.addMapping("/content/*");
}
}
The MidtierAppConfig class is empty, but the MidtierDispatcherConfig defines the static resources:
#Configuration
#ComponentScan("my.root.package")
#EnableWebMvc
public class MidtierDispatcherConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/courses/**/*")
.addResourceLocations("/WEB-INF/classes/content/")
;
}
}
Now when I want to have access to my #Controller's, I use the /data/ prefix, and when I want to access to my resources, I use the /content/ prefix. Caveat is that if I have a #RequestMapping("/app") class which has a #RequestMapping("/about") method, then both the data/app/about and the content/app/about will call just that method (and without actually trying I guess I might access the resources as /app/courses/whatEverPath too), because the dispatcher listends to both "data/" and "content/", and analyzes only the rest of the url ("app/about" in both cases) to find the proper #Controller.
Regardless, the current solution I've reached is satisfactory enough for me, so I will leave it as it is.
This worked for me. Files available at /resources/js/select.js. Watch out that you are not missing #EnableWebMvc annotation....
#EnableWebMvc
#EnableTransactionManagement
public class ApplicationContextConfig extends WebMvcConfigurerAdapter {
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}
I am using Spring Boot 2.2.1 and with "spring-boot-starter-web" and "spring-boot-starter-tomcat". In my case the Spring Boot only could find the "/resources/" folder when I use empty "classpath:/"
My folder structure:
My code:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("classpath:/");
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
And using the browser I can find any file like:
http://localhost:8080/resources/test.txt
http://localhost:8080/resources/images/background-1.png
It is possible to simplify the web page's URI just to contain the filename of a resource:
<link href="bootstrap.css" rel="stylesheet" media="screen">
An appropriate configuration might be as the following:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("*.css").addResourceLocations("/resources/css/");
}
Spring concatenates '/resources/css/' string with whatever filename extracted from URI to identify the actual location of a resource.

Categories

Resources