Spring Rest API and static web content access without suffix - java

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.

Related

Return HTML page from Spring controller

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";

Spring Boot doesn't render views properly

I've always build Spring MVC web application configuring them by myself until I've been told about this Spring Boot that helps you configuring/initialising your application.
So I decided to give it a try and I created a new project using Spring Boot. I followed all the steps in order to make it work with a main class a controller and all the required dependencies. When I run the application it starts and it responds also to the HTTP requests. The problem occurs when I try to return a view. It simply returns the view as a normal html file without rendering all the Spring tags.
Let me show you what I've done:
This is the directory tree of the project:
I created a directory webapp/WEB-INF/view in which i put the jsp files corresponding to the views. So far I have just one, helloWord.jsp that is quite simple:
<!DOCTYPE html>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html lang="en">
<body>
<div>
<div>
<h2>Hello ${name}</h2>
</div>
</div>
</body>
</html>
SpringBootHelloWorldApplication is the main class, containing the following code:
#SpringBootApplication
#ComponentScan(basePackages={"com.example.springboothelloworld"})
public class SpringBootHelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHelloWorldApplication.class, args);
}
}
ApplicationCofigurerAdapter configures the InternalResourceViewResolver in this way:
#Configuration
#EnableWebMvc
public class ApplicationConfigurerAdapter extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver internalResourceViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".jsp");
return bean;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
HelloWorldController is the controller that handles all the HTTP requests
#Controller
#RequestMapping("/home")
public class HelloWorldController {
#RequestMapping("/hello")
#ResponseBody
public String hello(){
return "hello";
}
#RequestMapping("/hello/{name}")
public String helloView(#PathVariable("name") String name, Model model){
model.addAttribute("name", name);
return "helloWorld";
}
}
And inside the application.properties I put these lines but I don't know if I really need them cause they are already present inside the ApplicationConfigurerAdapter.
spring.mvc.view.prefix= /WEB-INF/view/
spring.mvc.view.suffix= .jsp
When I send this HTTP GET request: http://localhost:8080/home/hello/Stefano
It returns this:
Which is of course an unexpected result.
I figured out how to make it work. I had to install maven on my macOS and then run the following command in order to remove the target folder, compile the code and also package it (in my case in a JAR format).
mvn clean package spring-boot:repackage
After this I can execute the JAR file using the following command:
java -jar target/file_name.jar
At this point (if everything went fine) my web app is now running on localhost:8080.
I still don't know how to make it work on IntelliJ also because in this way I don't know how to debug it.

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 (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