I have created the following Controller class:
package com.bilitutor.cct.control;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.servlet.ModelAndView;
import com.bilitutor.cct.bean.*;
import com.bilitutor.cct.service.*;
/**
* Handles requests for the application home page.
*/
#Controller
public class HomeController {
#Autowired
UserService userService;
#ModelAttribute("user")
public User getUserObect() {
return new User();
}
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Landing page. Just return the login.jsp
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(ModelMap model) {
logger.info("home() called");
model.addAttribute("emailErrorMessage", "Please enter Email");
return "login";
}
/**
* New User signup. If user already exists, send back the error, or send an email and forward to the email validation page
*/
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String signup(ModelMap model, #ModelAttribute("user")User user, BindingResult result) {
logger.info("signup() : email="+user.getEmail()+" pass="+user.getPassword()+" accessCode="+user.getAccessCode());
try {
userService.addUser(user);
} catch (DataIntegrityViolationException e) {
logger.info("Email already exists");
model.addAttribute("emailErrorMessage", "Email already exists");
}
return "login";
}
}
And the following JSP
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<body>
<form:form id="mainForm" method="post" commandName="user">
<table id="mainTable">
<tr id="emailRow"><td rowspan="2" class="lrPadding">Email</td><td class="lrPadding"><form:input id="email" path="email" class="editable"/></td></tr>
<tr id="emailErrorRow"><td id="emailError" class="errorMessage">${emailErrorMessage}</td></tr>
</table>
</form:form>
</body>
</html>
When I load the page for the first time, I can see the message "Please Enter Email" on my login.jsp . Upon hitting the /signup method with an existing email, I can see the "Email already exists" log message on the login page thus returned, but the message "Please Enter Email" stays. Can someone figure out the reason. Is it possible that the login.jsp is compiled only once and gets cached somewhere when I call it for the first time, and what I am seeing as a result of hitting /signup is the cached page. If so, how can I get around this.
The html of the login page that appears after hitting the signup() method is:
<html style="background-image:url('/cct/resources/images/wallpaper.jpg')">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="/cct/resources/css/master.css" type="text/css" media="screen" />
<script type="text/javascript" src="/cct/resources/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/cct/resources/js/login.js"></script>
<title>BiliTutor Content Creator Toolkit</title>
</head>
<body>
<h3> emailErrorMessage = Please enter Email</h3>
<form id="mainForm" action="/cct/" method="post">
<table id="mainTable" class="contents roundBox">
<tr id="logoRow"><td colspan="2" style="text-align:center;padding:20px;width:300px;height:100px"><img id="imgLogo" src="/cct/resources/images/logo.png"></td></tr>
<tr id="emailRow"><td rowspan="2" class="lrPadding">Email </td><td class="lrPadding"><input id="email" name="email" class="editable" type="text" value=""/></td></tr>
<tr id="emailErrorRow"><td id="emailError" class="errorMessage">Please enter Email</td></tr>
<tr id="passwordRow"><td rowspan="2" class="lrPadding">Password</td><td class="lrPadding"><input id="password" name="password" class="editable" type="password" value=""/></td></tr>
<tr id="passwordErrorRow"><td id="passwordError" class="errorMessage">Password should be atleast 8 char long</td></tr>
<tr id="confirmPasswordRow"><td rowspan="2" class="lrPadding">Confirm<br/>Password</td><td class="lrPadding"><input id="confirmPassword" type="password" class="editable"/></td></tr>
<tr id="confirmPasswordErrorRow"><td id="confirmPasswordError" class="errorMessage">Passwords dont match</td></tr>
<tr id="accessCodeRow"><td rowspan="2" class="lrPadding">Access<br/>Code</td><td class="lrPadding"><input id="accessCode" name="accessCode" class="editable" type="text" value=""/></td></tr>
<tr id="accessCodeErrorRow"><td id="accessCodeError" class="errorMessage">Access Code must be atleast 8 char long</td></tr>
<tr id="buttonsRow"><td colspan="2"><table style="margin:0px;margin-top:1em;padding:0px;width:100%"><tr>
<td><div id="btnNewUser" class="button" style="float:left">New Tutor<br/>Signup</div></td>
<td><div id="btnForgotPassword" class="button">Forgot<br/>Password</div></td>
<td><div id="btnLogin" class="button" style="float:right">Tutor<br/>Login</div></td>
</tr></table></td></tr>
</table>
</form>
</body>
</html>
and my IDE looks like:
I would change the method signature to
public String signup(ModelMap model, #ModelAttribute("user")User user, BindingResult result) {
add your error directly to the model
} catch (DataIntegrityViolationException e) {
logger.info("Email already exists");
model.addAttribute("emailErrorMessage", "Email already exists");
}
return "login";
and then access it like so in the jsp
<tr id="emailErrorRow"><td id="emailError" class="errorMessage">${emailErrorMessage}</td></tr>
I would recommend to use BindingResult result instead of an own error map:
bindingResult.rejectValue("email", "errorMessageCode", "Email already exists");
Related
I am trying to create a simple Spring project where restaurants can add menu items to shared database and users can use a html form to search the dishes based on a range of criteria- especially dietary requirements
Form example:
Restaurant Name: Chez Hans
Gluten Free: (X)
Egg Free: (X)
Vegan: ()
Example SQL command
Select all FROM "dishes" Dish WHERE restaurant_name = "Chez Hans" AND egg_free = TRUE AND
gluten_Free = TRUE;
A list of dishes that fit their criteria would then be returned to the user.
Any field in the form can be left blank, and not checking a box for example, "vegan" does not mean that criteria should be set as 'false', but rather not included within the query.
Because of this it seemed the best way to handle the issue was using JpaSpecificationExecutor to create dynamic SQL queries (similar to the implementation in the answer to the problem below)
Filtering database rows with spring-data-jpa and spring-mvc
I have created a solution based on my research and prior knowledge. However, when I implement my solution, no dishes are returned- even though there are dishes in the database that fit the search criteria. No errors are being produced, but simply a blank table, so I am not sure where I am going wrong.
I have researched countless articles/videos regarding JpaSpecificationExecutor/dynamic queries but there are parts of the that theory I am still unsure about. This is what I gather about JpaSpecificationExecutor/dynamic queries (but I may be wrong)
The meta model is not need to create dynamic queries but to verify the correctness of database query statements
To create queries using meta-model classes a wrapper class is required (In my example- DishSearch)
The following lines are to cast metamodel SingularAttribute class back to the original class value.
Path dname = root.get(Dish_.dname);
Path vegan= root.get(Dish_.vegan);
I am quite new to Spring and still finding it pretty difficult. Any help or advice would be very much appreciated!
Please see below my DishSpecification class:
package com.bron.demoJPA.specification;
public class DishSpecification implements Specification<Dish> {
private final DishSearch criteria;
public DishSpecification(DishSearch ds) {
criteria =ds;
}
#Override
public Predicate toPredicate(Root<Dish> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
Path<String> dname = root.get(Dish_.dname);
Path<Boolean> vegan= root.get(Dish_.vegan);
Path<Boolean> eggFree= root.get(Dish_.eggFree);
Path<Boolean> glutenFree= root.get(Dish_.glutenFree);
final List<Predicate> predicates = new ArrayList<Predicate>();
if(criteria.getDname()!=null) {
predicates.add(cb.equal(dname, criteria.getDname()));
}
if(criteria.isVegan()!=false) {
predicates.add(cb.equal(vegan, criteria.isVegan()));
}
if(criteria.isEggFree()!=false) {
predicates.add(cb.equal(eggFree, criteria.isEggFree()));
}
if(criteria.isGlutenFree()!=false) {
predicates.add(cb.equal(glutenFree, criteria.isGlutenFree()));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
Please see my DishSearch Class:
package com.bron.demoJPA.specification;
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
public class DishSearch {
private Long dishId;
private String dname;
private String description;
private double price;
private boolean vegan;
private boolean glutenFree;
private boolean eggFree;
private AppUser app;
}
Please see my SearchController Class:
#Controller
public class SearchController {
#Autowired
DishRepository drep;
#GetMapping("/showSearchForm")
public String showNewDishForm(Model model) {
// Create model attribute to bind form data
DishSearch dishSearch = new DishSearch();
model.addAttribute("dishSearch", dishSearch);
return "search_Dish";
}
#PostMapping("/showDishList")
public String saveUser(#ModelAttribute("dishSearch")DishSearch dishSearch) {
Specification<Dish> spec = new DishSpecification(dishSearch);
drep.findAll(spec);
return "show_dish_List";
}
}
Please see my DishRepository Class:
#Repository
public interface DishRepository extends JpaRepository<Dish, Long>, JpaSpecificationExecutor<Dish>{
#Transactional
#Modifying
List<Dish> findAll(Specification<Dish> spec);
}
Please see my search_Dish.html Thymeleaf Template:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Dish Management System</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<br>
<div class="col-sm-10 offset-sm-1 text-center">
<div class="container">
<h2> Manage Dishes </h2>
<hr>
</div>
<form action="#" th:action="#{/showDishList}" th:object="${dishSearch}" method="POST">
<div class="col-sm-10 offset-sm-1 text-center">
<input type="text" th:field="*{dname}"
placeholder="Dish Name" class="form-control mb-4 col-10">
</div>
<div class="form-check form-check-inline">
<label class=" form-check-label" for="inlineCheckbox1 ">Vegan?</label>
<input type="checkbox" th:field="*{vegan}" />
<label class="form-check-label" for="inlineCheckbox1">Gluten Free?</label>
<input type="checkbox" th:field="*{glutenFree}" />
<label class="form-check-label" for="inlineCheckbox1">Egg Free?</label>
<input type="checkbox" th:field="*{EggFree}" />
</div>
<br>
<br>
<br>
<br>
<button type="submit" class="btn btn-info col-4"> Search Database</button>
</form>
</div>
<hr>
</body>
</html>
Please see my show_dish_List.html Thymeleaf Template:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Search Results</title>
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
</head>
<body>
<br>
<div class="col-sm-10 offset-sm-1 text-center">
<h1>Dish List</h1>
</div>
<table border="1" class="table table-striped table-responsive-md">
<thead>
<tr>
<th>Dish Name</th>
<th>Dish description</th>
<th>Dish Price</th>
<th>Restaurant</th>
</tr>
</thead>
<tbody>
<tr th:each="dishSearch : ${listDishSearch}">
<td th:text="${dishSearch.dname}"></td>
<td th:text="${dishSearch.description}"></td>
<td th:text="${dishSearch.price}"></td>
</tr>
</tbody>
</table>
<div class="col-sm-10 offset-sm-1 text-center">
<a th:href="#{/showNewDishForm}"
class="btn btn-primary btn-sm mb-3"> Search Again</a>
</div>
----------------------------Update------------------------------
In addition to the answer provided below, in the Dish Specification Class I changed
if(criteria.getDname()!=null) {
predicates.add(cb.equal(dname, criteria.getDname()));
}
to
if(criteria.getDname()!="") {
predicates.add(cb.equal(dname, criteria.getDname()));
}
and the search is working fine now!
I believe the issue is that you are not adding the result in the Model which is being used to render the page show_dish_List.html, therefore nothing is being populated in the UI. Your UI is expecting the data to be in listDishSearch and there is nothing in that variable.
Update your code to:
#PostMapping("/showDishList")
public String saveUser(#ModelAttribute("dishSearch") DishSearch dishSearch, Model model) {
Specification<Dish> spec = new DishSpecification(dishSearch);
model.addAttribute("listDishSearch", drep.findAll(spec));
return "show_dish_List";
}
and everything should be working fine.
Remove the method findAll from your DishRepository repository. It is already being provided by the interface JpaSpecificationExecutor.
This is my code. I'm trying to add values to my table using JSP and MYSQL. Name of the table and database is correct.
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%# page import ="java.sql.*" %>
<%# page import ="javax.sql.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Inserting Data from form</title>
</head>
<body>
<h1> Insert a Record</h1>
<form method="post" action="insertrecord.jsp">
<table>
<tr>
<td>
<input type="text" name="uname">
</td>
<td>
<input type="password" name="upass">
</td>
<tr>
<tr>
<td><input type="submit" name="submit" value ="submit"></td>
</tr>
</table>
</form>
<%
String sub = request.getParameter("submit");
if(sub!=null)
{
String uname=request.getParameter("uname");
String password = request.getParameter("upass");
if(uname!=null && password!=null)
{
try
{
Connection con;
Class.forName("com.mysql.jdbc.Driver").newInstance();
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo","root","");
Statement stmt = con.createStatement();
int i = stmt.executeUpdate("insert into user values ('"+uname+"','"+password+"')");
out.println("Record Inserted Successfully");
stmt.close();
con.close();
}
catch(SQLException e)
{
out.println(e.getMessage());
}
}
}
%>
</body>
</html>
When I click on submit. I'm getting this error:
HTTP Status 404 – Not Found
Type Status Report
Message /something/insertrecord.jsp
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
Apache Tomcat/9.0.5
I have a textbox whose value I want to set with the value I get as a parameter in the request from user.
For example, the request is :
localhost:8084/quiz/login.jsp?uname=manish
I want to display "manish" in the textbox in the form.
How can I do that?
Here is the full code of my login.jsp
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Page</title>
</head>
<body>
<%
String error=(String)request.getParameter("error");
String uname=request.getParameter("uname");
if(error!=null)
{
if(error.equalsIgnoreCase("no_username_password"))
out.println("Username and password can't be empty");
if(error.equalsIgnoreCase("no_username"))
out.println("Username is required");
if(error.equalsIgnoreCase("no_password"))
out.println("Password is required");
}
%>
<form method="post" action="LoginServlet">
UserName: <input name="Username"><br>
Password: <input name="password"><br>
<input type="submit">
</form>
</body>
</html>
and this is the code of my servlet
package com.util.Servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet implementation class LoginServlet
*/
#WebServlet(description = "Will take username and password from login.html and validate user", urlPatterns = { "/LoginServlet" })
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String username=request.getParameter("Username");
String password=request.getParameter("password");
//PrintWriter out=response.getWriter();
//System.out.println(request.getAttribute("Error"));
//HttpSession session=request.getSession();
if((username=="" || username==null)&&(password=="" || password==null) )
{
response.sendRedirect("Login.jsp?error=no_username_password");
}
else if((username=="" || username==null)&&(password!=""||password!=null))
{
response.sendRedirect("Login.jsp?error=no_username");
}
else if((username!="" || username!=null)&&(password==""||password==null))
{
String url="Login.jsp?error=no_password&uname="+username;
response.sendRedirect(url);
}
}
}
You can get the request parameter value and print it out using the <c:out> taglib. ${param} is request parameter object and uname is your parameter name.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<input name="Username" value="<c:out value="${param.uname}"/>">
Following what you currently have in your JSP file you can do it like:
<input name="Username" value="<%= uname %>">
I'm not an jsp developer but i think you must do something like this in your HTML:
UserName: <input name="Username" value="<%= request.getParameter("uname") %>"><br>
<script type="text/javascript" src="${pageContext.request.contextPath}/resources/js/jquery.validate.min.js"></script>
<script>
function setHiddenVal(){
var goAhead = true;
var myVal="I am hidden value";
document.getElementById("secretValue").value = myVal;
if (goAhead == true) {
document.forms["register-form"].submit();
}
}
</script>
</head>
<body>
<!--Main Container Starts here-->
<div class="main_container">
<div class="header">
<div class="right_panel">
<h2 align="center"><u>User Master</u></h2>
<div class="top-form">
<div>
**<form:form action="/usermaster" modelAttribute="CustomerForm" id="register-form" method="POST">**
<table cellspacing="0" cellpadding="0" border="" class="form1">
<tr>
<td class="label">Name:</td>
<td>
<form:input path="firstname"/>
</td>
</tr>
<tr>
<td class="label">Password:</td>
<td>
<form:input path="password"/>
</td>
</tr>
</tbody>
</table>
<div>
<table>
<tr>
<td> </td>
<td>
<input type="button" class="btn blue px16" value="Search" />
<input type="button" name="submit" id="btnsubmit" value="Submit" onclick="setHiddenVal();"/>
<input type="button" class="btn blue px16" value="Clear" />
<input type="button" class="btn blue px16" value="Change Password" />
<input type="button" class="btn blue px16" value="Manage User Notification Profile" />
</td>
</tr>
</table>
</div>
</form:form>
</div>
</div>
<div class="clear"></div>
</div>
</div>
</div>
</body>
</html>
so above one is my code for jsp and below is the code of controller
#RequestMapping(value={"/usermaster" }, method = RequestMethod.POST)
public final String addUserMaster(#ModelAttribute("CustomerForm") CustomerForm pricing, Map<String, Object> map,
Model model, HttpServletRequest request) {
System.out.println("the first name is "+pricing.getFirstname());
System.out.println("the password is "+pricing.getPassword());
return "usermaster";
}
#RequestMapping(value={"/showusermaster" }, method = RequestMethod.GET)
public String showPage(ModelMap model){
model.addAttribute("CustomerForm", new CustomerForm());
return "usermaster";
}
But my page gets open up using a popup with the url:
C:\Users\ganganshu.s\AppData\Local\Microsoft\Windows\Temporary Internet Files\Content.IE5\YW6383E8\usermaster
so it should open like
http://localhost:8080/enbee/usermaster
Could you please tell me what should I put in the form action.. as I think some mistake is there in the form action does in spring MVC we put the action like in the case I mentioned above.
Spring confg file is given below :
<mvc:interceptors>
<bean class="com.enbee.admin.interceptor.AuthenticationInterceptor" />
<!-- Declare a view resolver-->
<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1" />
and the jsp name is usermaster.jsp
and in the sidemenu.jsp I have changed to this :
<li>User Master</li>
Change the method parameter of RequestMapping annotation to RequestMethod.POST:
#RequestMapping(value="/usermaster", method = RequestMethod.POST)
public final String addUserMaster(...){
...
}
so that when you submit your form to the URL /usermaster using method="POST" this method will get executed.
You also need to have a method (mapped to an URL) that will show this page to the user. You can use a method as below for this:
#RequestMapping(value = "/showusermaster", method = RequestMethod.GET)
public String showPage(ModelMap model){
model.addAttribute("CustomerForm", new CustomerForm());
return "usermaster";
}
With this method in place, the URL
http://localhost:8080/enbee/showusermaster
will show the usermaster.jsp page to the user. Now when you submit this form, the above addUserMaster method will be invoked.
You don't have to create new jsp file. The url /showusermaster will return the usermaster.jsp to the user, where the user can add form values and submit the form:
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
...
<c:url var="submitUrl" value="/usermaster">
<form:form id="form" action="${submitUrl}" modelAttribute="CustomerForm" method="POST">
Now when the user clicks on the submit button, this form will be submitted to /usermaster URL and will be handled by addUserMaster method.
Try to specify the content type returned by your controller method, adding produces = "text/html" param to your #RequestMapping annotation.
I am trying to run a simple Spring 3 MVC project to save form data but when I submit data the page goes to add.html with empty forms and no data is save in Mysql neither it is shown on the page.
Controller
package com.app.a;
/**
* Handles requests for the application home page.
*/
#Controller
#RequestMapping(value="/")
public class HomeController {
#Autowired
private Personrepo personRepo;
#RequestMapping(method=RequestMethod.GET)
public String showForm(ModelMap model){
List<Person> persons = personRepo.getAll();
model.addAttribute("persons", persons);
Person person = new Person();
person.setId(UUID.randomUUID().toString());
model.addAttribute("person", person);
return "home";
}
#RequestMapping(value="/add", method=RequestMethod.POST)
public ModelAndView add(#ModelAttribute(value="person") Person person,BindingResult result){
ModelAndView mv = new ModelAndView("home");
if(!result.hasErrors()) {
personRepo.add(person);
person = new Person();
person.setId(UUID.randomUUID().toString());
mv.addObject("person", person);
}
mv.addObject("persons", personRepo.getAll());
return mv;
}
#RequestMapping(value="/edit", method=RequestMethod.POST)
public ModelAndView edit(#ModelAttribute(value="person") Person person,BindingResult result) {
ModelAndView mv = new ModelAndView("home");
if(!result.hasErrors()) {
personRepo.edit(person);
person = new Person();
mv.addObject("person", person);
}
mv.addObject("persons", personRepo.getAll());
return mv;
}
#RequestMapping(value="/delete", method=RequestMethod.POST)
public ModelAndView update(#ModelAttribute(value="person") Person person,BindingResult result){
ModelAndView mv = new ModelAndView("home");
if(!result.hasErrors()) {
personRepo.delete(person.getId());
//personRepo.delete(person);
person = new Person();
mv.addObject("person", person);
}
mv.addObject("persons", personRepo.getAll());
return mv;
}
}
Home .jsp
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%#taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<body>
<table align="center">
<c:forEach items="${persons}" var="person">
<tr>
<td>Welcome:</td>
<td><c:out value="${person.firstName}" /></td>
</tr>
<tr>
<td>Your lastname:</td>
<td><c:out value="${person.lastName}" /></td>
</tr>
</c:forEach>
</table>
<form:form modelAttribute="person" method="post" action="add.html">
<form:hidden path="id" />
<table>
<tr>
<td><form:label path="firstName">First Name:</form:label></td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td><form:label path="lastName">Last Name</form:label></td>
<td><form:input path="lastName" /></td>
</tr>
<tr>
<td><form:label path="money">Money</form:label></td>
<td><form:input path="money" /></td>
</tr>
</table>
<input type="submit" value="Save" />
</form:form>
</body>
</html>
From the looks of it, unless you are doing content negotiation, you don't have a controller method mapped to the URL add.html.
Compare:
<form:form modelAttribute="person" method="post" action="add.html">
and
#RequestMapping(value="/add", method=RequestMethod.POST)
Make it
<form:form modelAttribute="person" method="post" action="add">
and you should be fine.