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.
Related
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";
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.
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.
I've checked around for this problem, but after 4 hours of trying many things, nothing worked for me.
I get a 405 error when trying to access my css file. Here are my Config.java
package com.myapp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
#Configuration
#ComponentScan("com.myapp")
#EnableWebMvc
#EnableTransactionManagement
public class Config extends WebMvcConfigurerAdapter {
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/","/css/","/js/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
and directories structure:
META-INF
WEB-INF
resources
|-css
| |-myapp.css
|-js
|-myapp.js
Using netbeans as IDE. I've also try to out the resources directory in WEB-INF. Same results.
Edit: my default controller below
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(ModelMap map) {
map.put("msg", "Hello Spring 4 Web MVC!");
return "index";
}
Edit 2: here is my WebinItalizer
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(Config.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
in this initializer, if I use "/" for servlet.addMapping("/") I can not have access to the css. But if I change it to something else like servlet.addMapping("/app") I can have access to the css. But the problem if I do this is all my url will start by app/ not very cool :/
First, if this is incorrect, sorry. I do a lot of Spring, but it's typically only XML configuration.
Let's start here:
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/","/css/","/js/");
This is saying "when I request something with the path http://localhost/resources/** look in /resources/, /css/ or /js/.
Well, /css/ and /js/ don't exist. Only /resources/ does.
In this case, you should map this
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
And access this way:
<script src="/resources/js/myapp.js"></script>
Alternately, you could just do this:
registry.addResourceHandler("/js/**")
.addResourceLocations("/resources/js/");
registry.addResourceHandler("/css/**")
.addResourceLocations("/resources/css/");
and access the static content this way:
<script src="/js/myapp.js"></script>
As for why changing the context root worked, it's due to this:
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
This attempts to route static content requests through the default servlet with the context root /. Since you're creating your DispatcherServlet at /, the default servlet will never be hit since it's set to the lowest priority by default.
If you're using registry.addResourceHandler you do not need to configure the default servlet and vice versa. As you discovered, you never hit it as by default it's the lowest priority.
If you still have issues, even after fixing the ResourceHandler, you can try omitting the resource handler configuration completely, or setting the default servlet to a higher priority (Which I wouldn't recommend).
The following solution worked for me. I added resource handlers to my configuration class which extends WebMvcConfigurerAdapter.
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/bootstrap/**")
.addResourceLocations("/bootstrap/");
}
Then, I imported my bootstrap.min.js file in my jsp as shown below :
<script type="text/javascript" src="<c:url value="/bootstrap/js/bootstrap.min.js"/>" ></script>
Note that bootstrap folder specified in the Resourcehandler resides under WebContent folder.
Edit :
After looking at your code in GitHub, I can clearly see that your resource handler has an extra * in it. It should look like below :
Clearly i don't see that you used <c:url> in your jsp. Do the following things and see if it works :
Remove the extra * from resource handler to look like below :
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
In your index.jsp file, change the link and script tags to look like this:
<script type="text/javascript" src="<c:url value="/resources/js/js.js"/>" ></script>
<link rel="stylesheet" href="<c:url value="/resources/css/site.css"/>" type="text/css">
Do not use context path ${cp} for script and link tags anymore. <c:url> is the reason this worked for me.
I have a Spring (4) MVC servlet running on Tomcat 8 in Eclipse. When I start tomcat, there are no errors in the console and all the correct request mappings for my controllers are logged. If I try to access localhost:8080/app/login my controller method executes (checked via debugging), but I get a 404 page with the following:
message /app/WEB-INF/jsp/login.jsp
description The requested resource is not available.
My project has the following directory structure:
project-root
|-src
|-WebContent
|-WEB-INF
|-jsp
|-login.jsp
My configuration class:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = "com.example")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/jsp/", ".jsp").viewClass(JstlView.class);
}
//Other stuff
}
Controller:
#Controller
#RequestMapping("/login")
public class AuthnRequestController {
#RequestMapping(value = "", method = RequestMethod.GET)
public ModelAndView getLoginPage() {
return new ModelAndView("login");
}
//Other stuff
}
The application was working fine in the past, but I was screwing around with my workspace/projects working on something else, and am unable to get this working again now that I'm coming back to it.
AFAIK, By default in a maven war project the jsp files are expected under /src/main/resources/. Since you have given a jsp file prefix of /WEB-INF/jsp/ in your config, please try moving the jsp files to the below location.
/src/main/webapp/WEB-INF/jsp/
Assumptions:
a mapping to root/WebContent is not provided in Web deployment assembly.
a mapping to /src/main/webapp is present in Web deployment assembly.
your eclipse is using maven war plugin