73

I am developing a website with Spring, and am trying to serve resources that are not .jsp files (.html for example)

right now i have commented out this part of my servlet configuration

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
        p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

And tried to return fromthe controller the full path to the resource.

@Controller
public class LandingPageController {

protected static Logger logger = Logger.getLogger(LandingPageController.class);

@RequestMapping({"/","/home"})
public String showHomePage(Map<String, Object> model) {
    return "/WEB-INF/jsp/index.html";   
   }
}

the index.html file exists in that folder.

NOTE: when I change the index.html to index.jsp my server now serves the page correctly.

Thank you.

rMonteiro
  • 1,371
  • 1
  • 14
  • 37
Gleeb
  • 10,773
  • 26
  • 92
  • 135
  • 2
    I think [this answer][1] might help. [1]: http://stackoverflow.com/questions/1483063/spring-mvc-3-and-handling-static-content-am-i-missing-something – CodeChimp Mar 18 '13 at 14:36
  • No this is not what i would like to do. i would like to have myself a folder just as i would if i would have programmed the website static content with notepad++. folder that holds my index.html and all content is relative to that index.html – Gleeb Mar 18 '13 at 15:00

9 Answers9

105

The initial problem is that the the configuration specifies a property suffix=".jsp" so the ViewResolver implementing class will add .jsp to the end of the view name being returned from your method.

However since you commented out the InternalResourceViewResolver then, depending on the rest of your application configuration, there might not be any other ViewResolver registered. You might find that nothing is working now.

Since .html files are static and do not require processing by a servlet then it is more efficient, and simpler, to use an <mvc:resources/> mapping. This requires Spring 3.0.4+.

For example:

<mvc:resources mapping="/static/**" location="/static/" />

which would pass through all requests starting with /static/ to the webapp/static/ directory.

So by putting index.html in webapp/static/ and using return "static/index.html"; from your method, Spring should find the view.

abhishek ringsia
  • 1,970
  • 2
  • 20
  • 28
andyb
  • 43,435
  • 12
  • 121
  • 150
  • 3
    Surprisingly this works. only with a small change that you need to return "static/index.html". – Gleeb Mar 18 '13 at 15:27
  • 2
    thousand of THANKS! it solved a lot of my problems on the project – DenisFLASH Aug 30 '14 at 11:53
  • 4
    Just a little remark. I don't even have to return "page.html" in a method, as there is NO NEED to have that method anymore. If by 'method' you meant a method of @Controller class which responds to GET request, we don't need them anymore, as with that `` we will intercept all requests to /static/... and redirect them directly to our resource in /webapp/static, thus not even calling "onHttpGet"-like method of a controller. So i commented that method, and it still works ;-) thanks – DenisFLASH Aug 30 '14 at 11:58
  • @andyb AFAIK, there's no such URL pattern definition as `**` in Servlet spec. You should use `/static/*` if you want to map all resources under `static` path. See here: http://stackoverflow.com/questions/13843294/what-does-the-double-wildcard-on-a-servlet-mapping-url-pattern-mean Please correct me if I am wrong. – smwikipedia Dec 03 '15 at 06:16
  • 1
    `**` is part of the Spring [Path Pattern Comparison](http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-pattern-comparison), although they kindly borrowed it from Apache Ant - see [AntPathMatcher](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html). It is perfectly valid to use `**` and in my answer it is used to match any subdirectory. Hope this helps but if not please comment or ask another question and I'd be happy to help out. – andyb Dec 03 '15 at 13:47
  • So it is a Spring implementation, rather than a Servlet specification. – andyb Dec 03 '15 at 13:55
  • @andyb, i faced with same problem but i couldn't solve it with what you said here, can u answer my post in this [link](http://stackoverflow.com/questions/38830960/the-requested-resource-is-not-available-when-wanna-map-to-html-file) – Lord ST Aug 08 '16 at 21:30
  • Still spring adds jsp, I got this error: HTTP Status 404 - /WEB-INF/index.html.jsp – zygimantus Jul 01 '17 at 14:53
  • It doesn't work for me. I have 404 status: The origin server did not find a current representation for the target resource or is not willing to disclose that one exists. I tried to put my static folder into webapp/static and webapp/WEB-INF/static but both failed. I use different type of return but doesn't help in any combination. Where might be a problem? – Tom Oct 02 '18 at 08:50
  • @Tom do you have any example code? Also which version of Spring are you using? This question and answer are over 5 years old so code might have changed since 2013! – andyb Oct 02 '18 at 14:18
  • @andyb I am using Spring MVC 4.0.3. Yes, I have example code but I can not put it here and new question would be a duplicate for sure. By the way with .jsp my project works fine. – Tom Oct 03 '18 at 08:23
8

I'd just add that you don't need to implement a controller method for that as you can use the view-controller tag (Spring 3) in the servlet configuration file:

<mvc:view-controller path="/" view-name="/WEB-INF/jsp/index.html"/>
Ivan
  • 1,477
  • 3
  • 18
  • 36
8

Background of the problem

First thing to understand is following: it is NOT spring which renders the jsp files. It is JspServlet (org.apache.jasper.servlet.JspServlet) which does it. This servlet comes with Tomcat (jasper compiler) not with spring. This JspServlet is aware how to compile jsp page and how to return it as html text to the client. The JspServlet in tomcat by default only handles requests matching two patterns: *.jsp and *.jspx.

Now when spring renders the view with InternalResourceView (or JstlView), three things really takes place:

  1. get all the model parameters from model (returned by your controller handler method i.e. "public ModelAndView doSomething() { return new ModelAndView("home") }")
  2. expose these model parameters as request attributes (so that it can be read by JspServlet)
  3. forward request to JspServlet. RequestDispatcher knows that each *.jsp request should be forwarded to JspServlet (because this is default tomcat's configuration)

When you simply change the view name to home.html tomcat will not know how to handle the request. This is because there is no servlet handling *.html requests.

Solution

How to solve this. There are three most obvious solutions:

  1. expose the html as a resource file
  2. instruct the JspServlet to also handle *.html requests
  3. write your own servlet (or pass to another existing servlet requests to *.html).

For complete code examples how to achieve this please reffer to my answer in another post: How to map requests to HTML file in Spring MVC?

Community
  • 1
  • 1
walkeros
  • 4,736
  • 4
  • 35
  • 47
7

You can still continue to use the same View resolver but set the suffix to empty.

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
    p:prefix="/WEB-INF/jsp/" p:suffix="" />

Now your code can choose to return either index.html or index.jsp as shown in below sample -

@RequestMapping(value="jsp", method = RequestMethod.GET )
public String startJsp(){
    return "/test.jsp";
}

@RequestMapping(value="html", method = RequestMethod.GET )
public String startHtml(){
    return "/test.html";
}   
Sashi
  • 1,977
  • 16
  • 15
  • as well i tried deleting the suffix all together. same deal. only works with the .JSP and not with .HTML – Gleeb Mar 18 '13 at 15:13
  • No. that this is a very interesting solution. are you sure this is working for you beyond the theory – Gleeb Mar 18 '13 at 15:26
  • @Sashi, can you take a look at my problem in this [link](http://stackoverflow.com/questions/38830960/the-requested-resource-is-not-available-when-wanna-map-to-html-file) – Lord ST Aug 08 '16 at 21:32
  • Does not work for me as well. Only with .jsp (Spring version: 4.3.14). – luis-br Mar 06 '18 at 23:11
1

I faced the same issue and tried various solutions to load the html page from Spring MVC, following solution worked for me

Step-1 in server's web.xml comment these two lines

<!--     <mime-mapping>
        <extension>htm</extension>
        <mime-type>text/html</mime-type>
    </mime-mapping>--> 
<!--     <mime-mapping>
        <extension>html</extension>
        <mime-type>text/html</mime-type>
    </mime-mapping>
 -->

Step-2 enter following code in application's web xml

  <servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.htm</url-pattern>
</servlet-mapping>

Step-3 create a static controller class

@Controller 
public class FrontController {
     @RequestMapping("/landingPage") 
    public String getIndexPage() { 
    return "CompanyInfo"; 

    }

}

Step-4 in the Spring configuration file change the suffix to .htm .htm

Step-5 Rename page as .htm file and store it in WEB-INF and build/start the server

localhost:8080/.../landingPage
Animesh
  • 11
  • 3
1

Java configuration for html files (in this case index.html):

@Configuration
@EnableWebMvc
public class DispatcherConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/index.html").addResourceLocations("/index.html");
    }

}
zygimantus
  • 3,649
  • 4
  • 39
  • 54
0

change p:suffix=".jsp" value acordingly otherwise we can develope custom view resolver

http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/web/servlet/view/UrlBasedViewResolver.html

AnilHoney
  • 259
  • 8
  • 20
0

It sounds like you are trying to do something like this:

  • Static HTML views
  • Spring controllers serving AJAX

If that is the case, as previously mentioned, the most efficient way is to let the web server(not Spring) handle HTML requests as static resources. So you'll want the following:

  1. Forward all .html, .css, .js, .png, etc requests to the webserver's resource handler
  2. Map all other requests to spring controllers

Here is one way to accomplish that...

web.xml - Map servlet to root (/)

<servlet>
            <servlet-name>sprung</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            ...
<servlet>

<servlet-mapping>
            <servlet-name>sprung</servlet-name>
            <url-pattern>/</url-pattern>
</servlet-mapping>

Spring JavaConfig

public class SpringSprungConfig extends DelegatingWebMvcConfiguration {

    // Delegate resource requests to default servlet
    @Bean
    protected DefaultServletHttpRequestHandler defaultServletHttpRequestHandler() {
        DefaultServletHttpRequestHandler dsrh = new DefaultServletHttpRequestHandler();
        return dsrh;
    }

    //map static resources by extension
    @Bean
    public SimpleUrlHandlerMapping resourceServletMapping() {
        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();

        //make sure static resources are mapped first since we are using
        //a slightly different approach
        mapping.setOrder(0);
        Properties urlProperties = new Properties();
        urlProperties.put("/**/*.css", "defaultServletHttpRequestHandler");
        urlProperties.put("/**/*.js", "defaultServletHttpRequestHandler");
        urlProperties.put("/**/*.png", "defaultServletHttpRequestHandler");
        urlProperties.put("/**/*.html", "defaultServletHttpRequestHandler");
        urlProperties.put("/**/*.woff", "defaultServletHttpRequestHandler");
        urlProperties.put("/**/*.ico", "defaultServletHttpRequestHandler");
        mapping.setMappings(urlProperties);
        return mapping;
    }

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();

        //controller mappings must be evaluated after the static resource requests
        handlerMapping.setOrder(1);
        handlerMapping.setInterceptors(this.getInterceptors());
        handlerMapping.setPathMatcher(this.getPathMatchConfigurer().getPathMatcher());
        handlerMapping.setRemoveSemicolonContent(false);
        handlerMapping.setUseSuffixPatternMatch(false);
        //set other options here
        return handlerMapping;
    }
}

Additional Considerations

  • Hide .html extension - This is outside the scope of Spring if you are delegating the static resource requests. Look into a URL rewriting filter.
  • Templating - You don't want to duplicate markup in every single HTML page for common elements. This likely can't be done on the server if serving HTML as a static resource. Look into a client-side *VC framework. I'm fan of YUI which has numerous templating mechanisms including Handlebars.
BreakerOfStones
  • 529
  • 5
  • 18
0

In case you use spring boot, you must not set the properties spring.mvc.view.prefix and spring.mvc.view.suffix in your application.properties file, instead configure the bean ViewResolver from a configuration class.

application.properties

# Configured in @Configuration GuestNav
#spring.mvc.view.prefix=/WEB-INF/views/
#spring.mvc.view.suffix=.jsp
# Live reload
spring.devtools.restart.additional-paths=.
# Better logging
server.tomcat.accesslog.directory=logs
server.tomcat.accesslog.file-date-format=yyyy-MM-dd
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.suffix=.log

Main method

@SpringBootApplication
public class WebApp extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(WebApp.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(WebApp.class, args);
    }

}

Configuration class

@Configuration
@EnableWebMvc
public class DispatcherConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/views/**").addResourceLocations("/views/");
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/notinuse/");
        viewResolver.setSuffix("");
        return viewResolver;
    }

}

A controller class

@Controller
public class GuestNav {

    @GetMapping("/")
    public String home() {
        return "forward:/views/guest/index.html";
    }
}

You must place your files in the directory /webapp/views/guest/index.html, be careful, the webapp directory is outside of the resources directory.
In this way you may use the url patterns of spring-mvc but serve static context.

Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92