JSP to Thymeleaf logic & forward syntax equivalent - spring-boot

In trying to move from JSP to Thymeleaf- in the former a Spring Boot initial class (with the main method) performed some validation on a user logging in, and if valid would present a banner.jsp. On agreeing to the banner.jsp's statement the user would click the "Ok" (on the banner.jsp), which called an index.jsp that used
<c:if test='${empty Validated}'><jsp:forward page="/do/Index" /></c:if>.
This ultimately brought up the webapp.
In converting to Thymeleaf I can get
<c:if test='${empty Validated}'><jsp:forward page="/do/Index" /></c:if> piece within the index (now .html). I'd figure something like
<div th:if="${empty Validated}" th:action="#{/do/Index}"></div>
might work, but it returns a 404 error. I'm wondering if I would , instead, need to call a controller. If that's the case I'm unsure of the syntax.
I've tried some syntax variations of the <div th:if="${empty Validated}" th:action="#{/do/Index}"></div> without success.
Index.html (as Thymeleaf)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<!--<c:if test='${empty Validated}'><jsp:forward page="/do/Index" /></c:if>-->
<div th:if="${empty Validated}" th:action="#{/do/Index}"></div>
</body>
</html>
I would want the forward functionality to be successfully mimicked in Thymeleaf so that the index.html opens the webapp
Controller
package mil.dfas.springmvc.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
* #author David Higgins
*/
#Controller
public class LMSSpringController {
private static final Logger log = LoggerFactory.getLogger(LMSSpringController.class);
#Value("${messages.banner:default-value}")
private String message = "HelWd";
#RequestMapping("/banner")
public String welcome(Model model) {
log.info("Config Example");
System.out.println("LMS Controller");
model.addAttribute("msg","Forward Handled");
return "banner";
}
/* #GetMapping("/index")
public String indexHandler() {
System.out.println("index cont");
return "forward:/do/Index";
}*/
#RequestMapping(value = "/index", method = RequestMethod.POST)
public ModelAndView indexHandler(ModelMap model) {
model.addAttribute("attribute", "index");
System.out.println("index cont");
return new ModelAndView("forward:/do/Index", model);
}
#RequestMapping("/")
public void handleRequest() {
throw new RuntimeException("test exception");
}
}

Related

What is the way to use variable expression in thymeleaf 2.5.6 version

I just create simple template by the help of thmeleaf, When I try to
access the variable from controller class by the help of variable
Expression , But I get Issue on variable expression ,In the time of
variable expression access . Showing below type of error
cannot Resolve variable name
My variable name is today which I define in my controller class
Homecontroller.kt
package com.nilmani.thymeleafdemo.controller
import org.springframework.web.bind.annotation.RestController
import org.thymeleaf.ITemplateEngine
import org.thymeleaf.context.WebContext
import java.text.SimpleDateFormat
import java.util.*
import javax.servlet.ServletContext
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
#RestController
class HomeController : IGTVGController() {
#Throws(Exception::class)
fun process(request:HttpServletRequest,response: HttpServletResponse,
servletContext: ServletContext,templateEngine: ITemplateEngine){
val dateForm:SimpleDateFormat= SimpleDateFormat("dd-mm-yyyy")
val calendar:Calendar = Calendar.getInstance()
val ctx : WebContext = WebContext(request,response,servletContext,request.locale)
ctx.setVariable("today",dateForm.format(calendar.time))
templateEngine.process("home",ctx,response.writer)
}
}
home.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Welcome TO our WebPage</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="#{/css/gtvg.css}" />
</head>
<body>
<p th:utext="#{home.welcome}">Welcome to our website</p>
<p>Today is: <span th:text= "${today}"></span></p>
</body>
</html>
error shows at this point below point
th:text= "${today}"
What is the reason for not support of variable expression . I already
added thymeleaf gradle depedency in my project.But the variable
expression not working
I don't know Kotlin, so I hope you can convert my Java answer to it.
I see you use #RestController, but if you are using Thymeleaf, use #Controller
You can have Spring Boot inject the Model class and add your attributes there
The easiest is to return a String value that represents the name of the Thymeleaf template.
Using all that, you get something like this:
#Controller
public class HomeController {
public String process(Model model) {
model.addAttribute("today", ...);
return "home"; //this refers to home.html like this
}
}
PS: You should not use Calendar anymore, see https://www.baeldung.com/java-8-date-time-intro for more info on the "new" date/time API introduced in Java 8.

unable to render an array of strings using th:each atrribute in thymeleaf

pls I tried rendering an array of strings with thymeleaf and its showing blank. does any one know the reason for this? The code is shown below:
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomeController {
//public String[] greetings= new String[] {"Hi","Hello","Watsup"};
#RequestMapping("/home")
public String getHomePage(Model model) {
model.addAttribute("msg",new String[]{"Hi","Hello","Watsup"});
//model.addAttribute("msg",new String[]{"Hi","Hello","Watsup"});
return "home";
}
//<h1 th:text=${welcomeMessage}>*Hello Peeps!*</h1>
}```
the html(thymeleaf) is shown below:
<h1 th:each="msg : ${greetings}" th:text=${msg}>Hello,homepage</h1>
You are setting the attribute on the model by the "msg" key. You need to access that instead of greetings.
<h1 th:each="message : ${msg}" th:text=${message}>Hello,homepage</h1>

Hardcoding URL in Spring

package com.example.servingwebcontent;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greeting(#RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
<!DOCTYPE HTML>
<html>
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Get your greeting here</p>
</body>
</html>
The examples from the official guide: https://spring.io/guides/gs/serving-web-content/
We can see that the url, that is greeting, is hardcoded.
Suppose, I write that url in 50 places. And now I want to change the url.
Is it somehow possible to escape hardcoding?
The HTTP request can be GET, POST, PUT, DELETE, and PATCH.
To map the HTTP request with your definition, you need to use #GetMapping, #PostMapping, #PutMapping, #DeleteMapping, and #PatchMapping in the controller class.
So, URL /greeting can be used in those 5 requests only. So, we can't write the same URL in 50 places in the controller class.

Spring using ModelAndView addObject

I am trying to display objects in a jsp page that are loaded using addObject() and returned via a controller. I am not seeing the objects in the jsp. Here is my controller:
import java.util.ArrayList;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import com.apress.pss.terrormovies.model.Movie;
import com.apress.pss.terrormovies.service.MoviesService;
#Controller
#RequestMapping("/movies")
public class MovieController {
#Autowired
private MoviesService moviesService;
... Other Mapped mehtods not shown ...
// Respond to http://localhost:8080/movies and require login
// Get a list of movie objects in an ArrayList and return to view
#RequestMapping(method = RequestMethod.GET, value="/")
public ModelAndView getAllMovies() {
ModelAndView mv = new ModelAndView("allMovies");
// Debug
for (Movie movie: moviesService.getAllMovies()) {
System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX " + movie.getName());
}
mv.addObject("movies", moviesService.getAllMovies());
return mv;
}
}
Here is my MoviesServiceImpl that implements moviesService.getAllMoivies()
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.access.prepost.PostFilter;
import com.apress.pss.terrormovies.model.Movie;
public class MoviesServiceImpl implements MoviesService {
private static final Map<String, Movie> MOVIES = new HashMap<String, Movie>();
static {
//System.out.println("----------------- Entering Die Hard");
MOVIES.put("die hard", new Movie("Die Hard", "20000000"));
// Create another movie for testing PostAuthorize in MoviesController
//System.out.println("----------------- Entering two days in paris");
MOVIES.put("two days in paris", new Movie("two days in paris", "1000000"));
}
... Other methods not shown....
// Allow ROLE_ADMIN to have access to movies with budget over 5000000. Other
// users will see only movies with budgets < 5000000
#PreAuthorize("isFullyAuthenticated()")
#PostFilter("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and T(java.lang.Integer).parseInt(filterObject.budget) < 5000000)")
public Collection<Movie> getAllMovies() {
return new ArrayList<Movie>(MOVIES.values());
}
}
Here is the jsp page I am using to display the results:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# taglib prefix="security"
uri="http://www.springframework.org/security/tags"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Terror movies</title>
</head>
<p>Movies:</p>
<body>
<c:if test="${not empty movies}">
<c:forEach items="${movies}" var="movie">
${movie.name}<br />
</c:forEach>
</c:if>
</body>
</html>
Finally, here is my Movies class:
public class Movie {
private String name;
private String budget;
public Movie(String name, String budget) {
super();
this.name = name;
this.budget = budget;
}
public String getName() {
return name;
}
public String getBudget() {
return budget;
}
public void setName(String name) {
this.name = name;
}
public void setBudget(String budget) {
this.budget = budget;
}
public String toString() {
return "Title: " + name + "; Budget: " + budget;
}
}
When I got to the URL /movies (on localhost) I am prompted for login. When I login as ADMIN I should see both the movies added into the MOVIES Map in MoviesServiceImpl. I can see the debug in the static block load the movies. I can see the movies accessed by the debug in the MovieController.getAllMovies() method. I am correctly directed to the allMovies.jsp page, but the only thing output is "Movies:". If I remove the check around the for loop in allMovies.jsp I get the following output: Movies: ${movie.name}. There are no exceptions thrown or other errors I can see, however I don't believe I am using ModelAndView.addObject() correctly. Some help would be appreciated. Thank you in advance.
Update: If I put the following statemnt in my jsp page: <% System.out.println("jsp: movie " + pageContext.findAttribute("movies")); %> I will get the following output: "jsp: movie [Title: Die Hard; Budget: 20000000, Title: two days in paris; Budget: 1000000]" So the Object array is getting to the jsp page, I am just not accessing it correctly but don't see the error.
Can you please check with Model.addAttribute(name, value) instead of ModelAndView.addObject(name, value)?
I think you should get the same problem with the Model.addAttribute approach as well.
Please try adding the following
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
to the JSP.
For those who may have a similar problem, the answer turned out to be with my web.xml file. This example is from the book Pro Spring Security. The author builds on previous examples to illustrate concepts. With this example, the author did not mention updating the web.xml file from its earlier versions which were using a DTD rather than an XML Schema. I had to change my web.xml from:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
...
</web-app>
To:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
...
</web-app>
The EL expressions were not being evaluated. Works fine now.
Resolved by modifying
<web-app>
as
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">

How to make localhost app publish to CloudFoundry as is: getting Resource not Available on CF

I have a basic Spring web app (Spring MVC Project) that I want to run on CloudFoundry. I took the default HelloWorld project and added to it. I've installed the CloudFoundry STS extensions, got a server created, publisd my app to the CF site. The 'home' page displays both on my localhost server, and the CF servers. All good. But, when I click on the only link to take me back into the HomeController to a different method/view, I get a 'Resource not available' error on the CF server, though it works perfectly on my localhost (local PC) server.
On my local PC:
The url is: http://localhost:8080/myapp (correct)
The initial page (home.jsp) displays with one link: Property (correct)
Mousing over the link shows this in the status bar: http://localhost:8080/myapp/property (correct)
Clicking takes me to the method mapped to /property and shows the property page (property.jsp). (correct)
On CloudFoundry:
The url is : http://myapp.cloudfoundry.com/ (correct)
The initial page (home.jsp) displays same as on my localhost PC. (correct)
Mousing over link shows this in status bar: http://myapp.cloudfoundry.com/myapp/property (correct, I think).
Clicking gets 'esource not available.
When I go up into the location window and remove myapp from the url, it works.
Below is all the code, but I think it's just some of my own misunderstanding of the two environments, my local PC, and CloudFoundry. Hopefully, someone can educate me on what I'm not knowing here to get the apps to work in both environements--locally, and on CloudFoundry.
Here is the HTML for home.jsp, the initial page
<html>
<head></head>
<body>
Property
</body>
</html>
The HomeController is:
package com.myapp.app;
import java.util.Locale;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.myapp.services.PropertyServicesImpl;
/**
* Handles requests for the application home page.
*/
#Controller
public class HomeController {
private static final String VIEW_HOME = "home";
private static final String VIEW_PROPERTY = "property";
private static final String ACQUISITIONS = "acquisitions";
#Autowired private PropertyServicesImpl propertyServices;
/**
* Shows home view
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Locale locale, Model model) {
return new ModelAndView(VIEW_HOME);
}
/**
* Shows Property.jsp with jQuery tabs.
*/
#RequestMapping(value = "/property", method = RequestMethod.GET)
public ModelAndView property(Locale locale, Model model) {
return new ModelAndView(VIEW_PROPERTY);
}
}
rather than putting a fixed value in your view it would be best to get the context path for the request and then adding that to the path in your view.
Add the following imports in to your Home controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
Then in the RequestMapping method get the current request object and create a UrlPathHelper instance and get the base path for the requests context;
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
UrlPathHelper helper = new UrlPathHelper();
String baseURL = helper.getContextPath(request);
So, when run from vFabric locally, baseURL will be "/myapp" and when run from a Cloud Foundry instance it will be ""
All that is left is to add this to the model and use it in the view;
model.addAttribute("relPath", baseURL);
I tested this with the Spring MVC template project in STS and it worked just fine, my HomeController looked like this;
package com.vmware.mvctest;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.sun.tools.internal.ws.processor.model.Request;
/**
* Handles requests for the application home page.
*/
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! the client locale is "+ locale.toString());
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
UrlPathHelper helper = new UrlPathHelper();
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
String baseURL = helper.getContextPath(request);
model.addAttribute("serverTime", formattedDate );
model.addAttribute("relPath", baseURL);
return "home";
}
}
and my view looked like this;
<%# page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world! (${relPath})
</h1>
home
<P> The time on the server is ${serverTime}. </P>
</body>
</html>
the context path in CF is {app.name}.cloudfoundry.com and not {app.name}.cloudfoundry.com/{app.name}
Replace in your jsp the Property with Property.

Resources