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

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.

Related

Default resource handler for an URL path serving a static file in Spring Boot MVC

I have a Spring Boot-based project (based on spring-boot-starter-parent and spring-boot-starter-web in which I want to both serve static content (built in another module, and copied into the project with maven-resources-plugin).
With the default configuration/conventions, it looks for static content in misc. places, like classpath:/static and classpath:/public, which is useful if the static content is to be served from the root URL, which for this project is not the case.
I want to serve it off /web/ which I have successfully done by deriving from WebMvcConfigurer (and annotated the class with #Configuration) and overridden the public void addResourceHandlers(ResourceHandlerRegistry registry) method; registry.addResourceHandler("/web/**").addResourceLocations("classpath:/static/web/");
This works when explicitly referring to the file names in the /web/-folder, but I can't figure out how I can emulate the "default document" behavior, e.g. index.html should be served for /web/. If you drop off an index.html in one of the root directories for static content, it will be handled by the WelcomePageHandlerMapping.
Found the solution; I also had to override the addViewControllers() to create an explicit mapping and fix the precedence:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/web/").setViewName("index.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}

Spring Boot static content with context path

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

Adding external static files (css, js, png ...) in spring boot

Background
I have a spring boot application which has the logo.png file added to the static folder of the resource file, which is eventually built into the jar file which is used in the execution.
This jar application need to be run in multiple instances for different clients. So what I did is create an external application.properties file which distinguish the settings for each users.
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Problem
But the problem is, i need to change the logo of each instance of my application. I cannot embed the customer logos into my application jar. Rather I need to keep it external like my application.properties.
For the moment, what I have done is check for the file logo.png in the same folder of jar of execution, and if excist, read the file, get base64 data and show it in the img tag.
But I want this to be done in a proper way as static content. I need the static content to be externalized. so I can let each customer have a specific instance of the jar running with different static resource content
For example. I need to keep the external static files as below and access from the urls in my view href or src attributes of the html tags.
Summary
Required folder structure
+ runtime
- myapp-0.1.0.jar
- application.properties
+ static
- logo.png
Should be able to access
<img th:src="#{/logo.png}" />
You can use resource handlers to serve external files - e.g.
#Component
class WebConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/ext/**").addResourceLocations("file:///yourPath/static/");
}
}
The WebMvcConfigurerAdapter is deprecated. As from Spring Boot 2.x you could use WebMvcConfigurer instead.
#Configuration
public class MediaPathConfig {
// I assign filePath and pathPatterns using #Value annotation
private String filePath = "/ext/**";
private String pathPatterns = "/your/static/path/";
#Bean
public WebMvcConfigurer webMvcConfigurerAdapter() {
return new WebMvcConfigurer() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern(pathPatterns)) {
registry.addResourceHandler(pathPatterns)
.addResourceLocations("file:" + filePath);
}
}
};
}
}

Spring - Path variable truncate after dot - annotation

I am trying to set up a REST endpoint that allows querying a user by their email address. The email address is the last portion of the path so Spring is treating foo#example.com as the value foo#example and truncating the extension .com.
I found a similar question here Spring MVC #PathVariable with dot (.) is getting truncated
However, I have an annotation based configuration using AbstractAnnotationConfigDispatcherServletInitializer and WebMvcConfigurerAdapter. Since I have no xml configuration, this solution will not work for me:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="useDefaultSuffixPattern" value="false" />
</bean>
I have also tried this solution which uses regex but it has not worked either.
#RequestMapping(value = "user/by-email/{email:.+}")
Does anyone know how to turn off the suffix pattern truncation without xml?
The dot in the path variable at the end of the URI causes two unexpected behaviours (unexpected for the majority of users, except those familiar with the huge number of Spring configuration properties).
The first (which can be fixed using the {email:.+} regex) is that the default Spring configuration matches all path extensions. So setting up a mapping for /api/{file} will mean that Spring maps a call to /api/myfile.html to the String argument myfile. This is useful when you want /api/myfile.html, /api/myfile.md, /api/myfile.txt and others to all point to the same resource. However, we can turn this behaviour off globally, without having to resort to a regex hack on every endpoint.
The second problem is related to the first and correctly fixed by #masstroy. When /api/myfile.* points to the myfile resource, Spring assumes the path extension (.html, .txt, etc.) indicates that the resource should be returned with a specific format. This behaviour can also be very useful in some situations. But often, it will mean that the object returned by a method mapping cannot be converted into this format, and Spring will throw a HttpMediaTypeNotAcceptableException.
We can turn both off with the following (assuming Spring Boot):
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// turn off all suffix pattern matching
configurer.setUseSuffixPatternMatch(false);
// OR
// turn on suffix pattern matching ONLY for suffixes
// you explicitly register using
// configureContentNegotiation(...)
configurer.setUseRegisteredSuffixPatternMatch(true);
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}
More about Content Negotiation.
You have to add trailing slash at the end of the path variable after name like
#RequestMapping(value ="/test/{name}/")
The Request like
http://localhost:8080/utooa/service/api/admin/test/Takeoff.Java#gmail.com/
I've found the solution to this using the ContentNegotiationConfigurer bean from this article: http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
I added the following configuration to my WebConfig class:
#EnableWebMvc
#Configuration
#ComponentScan(basePackageClasses = { RestAPIConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
By setting .favorPathExtension(false), Spring will no longer use the file extension to override the accepts mediaType of the request. The Javadoc for that method reads Indicate whether the extension of the request path should be used to determine the requested media type with the highest priority.
Then I set up my #RequestMapping using the regex
#RequestMapping(value = "/user/by-email/{email:.+}")
For the Java-Config folks:
With Spring 4 you can simply turn this feature off by:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}
Then in the whole application dots will treated as dots.

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