Return HTML page from Spring controller - java

I have a Spring Boot app setup as a REST api. I now also want to be able to serve simple HTML pages to the client, without the use on any template engine like Thymeleaf. I want access to the HTML pages to fall under the same security constraints setup by Spring Security with the use of WebSecurityConfigurerAdapter, already present in my app.
What I've tried is having a Controller:
#Controller
public class HtmlPageController {
#RequestMapping(value = "/some/path/test", method = RequestMethod.GET)
public String getTestPage() {
return "test.html";
}
}
and placing the test.html file in /resources/test.html or /webapp/WEB-INF/test.html.
Every time I try to access the page at localhost:8080/some/path/test a 404 is returned.
How do I make this work?

Okey so apparently Spring Boot supports this without any additional configuration or controllers.
All I had to do was to place the HTML file in the correct directory /resources/static/some/path/test.html and it can be reached at localhost:8080/some/path/test.html.
In my attempts to change the directory from which the file is served I was unsuccessful. It seems that providing a separate #EnableWebMvc (needed for configuring the resource handlers) breaks the Spring Boot configuration. But I can live with using the default /static directory.

There is a Spring MVC mecanism that exists to provide static resources.
In the config class, overide this method :
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("some/path/*.html")
.addResourceLocations("/static/");
}
And place your html files in the src/main/webapp/static/ folder.
If you request some/path/test.html (note the .html), it will return the test.html file located in static folder.
You can obviously use a different folder or a more sofiticated directory structure.
This way you don't have to create a controller. Note that your config class should implements WebMvcConfigurer.

Your html, js and css files should be under the src/main/resources/static directory. and your return statement you can try removing .html.
#RestController
public class HtmlPageController {
#GetMapping("/some/path/test")
public String getTestPage() {
return "test";
}
}

See tutotrial example how to define html view in Spring MVC configuration
#Bean
public InternalResourceViewResolver htmlViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setPrefix("/WEB-INF/html/");
bean.setSuffix(".html");
bean.setOrder(2);
return bean;
}
setOrder is set to 2 because it include also JSP support in example
Also you need to change to return without .html suffix
return "test.html";

Related

Spring Boot static resources are not available

Files in resources/static are not available. How I can fix it?
Hierarchy:
resources/
db/
static/
image.png
templates/
application.properties
But if I open localhost:8081/image.png I get error.
My WebConfig:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("/webjars/");
}
}
You should add below line to your existing resource mapping:
registry.addResourceHandler("/**")
.addResourceLocations("resources/static/");
You have configured the webjars resourceHandler to serve client side script or stylesheet dependencies. but custom handler is not added to serve your image file.
Since you are overriding the addResourceHandlers (ResourceHandlerRegistry registry) method, you should provide all the resourceLocation and handler mapping in your implementation.
Please check serving-static-web-content-with-spring-boot article to have more clear idea.
Note:
If you are using spring-boot you shouldn't be overriding the above method untill explicitly required as its already taken care in WebMvcAutoConfiguration.java.
Please check below default implementation:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache()
.getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(
this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
}
Here give path of your image:
registry.addResourceHandler("/**").addResourceLocations("file:/path/to/your/image/");
If you would prefer to serve static content (including web pages, js, pdf, css, pdf, doc etc.) from outside of the WAR, may be that is usefull. if you wanna changed any that static contents, just you can put changed file to your content path on application server easly and serving that without deploying or restarting you application server. If you prefer this way, you can do it with a small configuration on your application server.

RequestMapping URL pattern ending with jsp

I have a Controller in Spring Boot application
#Controller
public class MyController {
#RequestMapping("/vues/*.jsp")
public String views(HttpServletRequest request) {
return ((String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).substring(1);
}
}
And in application.properties
server.context-path=/myapp
spring.mvc.view.prefix=/WEB-INF/myapp/
The goal is to dispatch all my jsp in the WEB-INF/myapp/vues/ directory and to keep the exact comportement of the older app with .jsp URL
My app should work like this:
The user call http://myapp/vues/page.jsp
My app receive the call and find the handler in my controller because the URL match the RequestMapping url pattern
My handler return vues/pages.jsp
Spring Boot add /WEB-INF/myapp/ to find the good JSP file
I've already developed several Spring Boot apps which work perfectly, with JSP and so on. But it seems in that case, that url pattern ending with JSP won't work.
I try in the same app with #RequestMapping("/vues/*"), spring.mvc.view.suffix=.jsp in application.properties and a call to the same url but without .jsp at the end and it work.
Is there a solution to configure Spring Boot to accept *.jsp by RequestMapping?
NOTE: I've seen lots of posts talking about this subject but none of them answer to this question
UPDATE: I continue to develop without the ending jsp, but in the code I cover, there are calls to response.sendRedirect("../vues/view1"); or request.getRequestDispatcher("../vues/view1.jsp") and request.getRequestURI().endsWith("/vues/view1.jsp");. Everything seems a bit confusing. I know (beacause it was already done in the past!) is it posible to manage URL with ending .jsp. Why isn't this more popular? Why is it such a problem in springboot? Must I edit a web.xml file to manage this?
You need to enable the default servlet in one class of configuration. You can do this by this way:
#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();
}
}
After this you can use #RequestMapping("/some-mapping") without specifing the .jsp termination.

Spring Rest API and static web content access without suffix

Let's assume I have Spring Rest API called application and its requests mapped on /api . It means I call for example GET method to get list of users:
localhost:8080/application/api/users
Working well. My goal is to have simple static html files alongside this API able to refer to each other. I need to find the index.html file and make it as the homepage.
localhost:8080/application/
It correctly shows me index.html using:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String homePage(ModelMap model) {
return "home";
}
and
#Configuration
#ComponentScan(basePackages = "net.nichar.application")
#EnableWebMvc
public class ApplicationConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".html");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
Where I struggle is to navigate with <a href=...> over another files in the same folder index2.html, index3.html without need to explicitely write the suffix html. I try to achieve to access the webpages like
localhost:8080/application/index2
without using another #RequestMapping (except the first one mapping the home page).
One more question, is there a way to "skip" a folder in the path navigation? For clarity, I want to put these html files to webapp/static folder, however I have to access them like
localhost:8080/application/static/...
I have tried to follow a number of tutorials shortly about the Spring resources mapping, however noone of them described the solution of any similar problem. I don't use Spring Boot.
Thank you for any help.
Shortly:
How to access files in --> with:
webapp/WEB-INF/pages/index.html --> localhost:8080/application
webapp/static/index2.html --> localhost:8080/application/index2
webapp/static/index3.html --> localhost:8080/application/index3
You can use something like that,
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/welcome").setViewName("welcome");
registry.addViewController("/about").setViewName("about");
registry.addViewController("/contact").setViewName("contact");
}
where login is mapped to login.html, and welcome is mapped welcome.html. It does not require #RequestMapping, but still require an explicit mapping.

Spring (with Jade) Resources

The Problem
My spring-boot application recently changed routing from host/endpoint to host/middle/endpoint. Since the change, I am running into an issue where the resources are not being found relative to the new url structure. Before, I could reference resources like css stylesheets like link(rel='stylesheet', href='css/style.css'), but now the logger shows an error saying it can't find the resource at /middleman/css/style.css.
From my research, I have found that what I need to do is use a resource handler registry. I have created one (as shown below) but it doesn't seem to be working. I think the problem is that even though I now have the resource registry, I am not referencing resources in the registry. What is the proper way to solve this problem and have all resources point load from the same place regardless of the endpoint? I very well may be missing some obvious piece of SOP
Note: This is all a dumbed down representation of my project in order to give the idea of what is going on without giving unnecessary information.
Project Structure
src
main
java
com.mystuff.cool
configurations
ResourceConfiguration.java
controllers
RoutingController.java
application
Application.java
resources
static
css
footer.css
style.css
images
place1.png
location1.png
spot1.png
favicon.ico
javascripts
layout.js
templates
home.jade
Application Class
#ComponentScan(basePackages = {"my.packages"})
#EnableAutoConfiguration
#EnableSAMLSSO
#Configuration
public class Application
{
public static void main(String[] args)
{
SpringApplication.run(new Object[]{ Application.class, ServiceConfig.class, ResourceConfiguration.class}, args);
}
}
Resource Configuration
#EnableWebMvc
#Configuration
public class ResourceConfiguration extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
{
configurer.enable();
}
}
Controller
#Controller
public class RoutingController
{
#RequestMapping("/house/home")
public String home(Model model)
{
model.addAttribute("title", "Home is where the heart is");
commonModelTribs(model);
return "home";
}
}
Home Page
doctype html
html
title Place-spedia #{title}
link(rel='icon', href='images/favicon.ico')
link(rel='stylesheet', href='css/style.css')
script(src='javascripts/layout.js')
link(rel='stylesheet', href='css/footer.css')
body
div#footer-icons
a(href='place1')
img#place1(src="images/place1.png")
a(href='location1')
img#location1(src="images/location1.png")
a(href='spot1')
img#spot1(src='images/spot1.png')
If you are using spring boot, you don't need to worry about the resource configuration since you are already configuring the resource directory through the auto configuration. The default behavior for the autoconfiguration is to look within resources/static.
Your issue is with your href values, try inserting a leading forward slash:
link(rel='icon', href='/images/favicon.ico')
link(rel='stylesheet', href='/css/style.css')
script(src='javascripts/layout.js')
link(rel='stylesheet', href='/css/footer.css')
Spring is routing your application to a new relative path, so by putting the leading / in your href attributes, you are telling the router to look absolutely within the static directory instead of relatively from the middle directory.

How to serve .html files with Spring

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.
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.
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"/>
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:
get all the model parameters from model (returned by your controller handler method i.e. "public ModelAndView doSomething() { return new ModelAndView("home") }")
expose these model parameters as request attributes (so that it can be read by JspServlet)
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:
expose the html as a resource file
instruct the JspServlet to also handle *.html requests
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?
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";
}
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
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");
}
}
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
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:
Forward all .html, .css, .js, .png, etc requests to the
webserver's resource handler
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.
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.

Categories