Reputation: 354
I'm new to Spring MVC and is making a simple todo web app. I'm getting the following error while binding data. I believe there is something with the jsp file with the Springmvc form where it messed up the binding process. I assume the bindingresult will return the form again but it does not for some reason.
org.apache.jasper.JasperException: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'todo' available as request attribute
org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:549)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:465)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
My updateToDo.jsp form
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>
<head>
<title>update task</title>
<link href="webjars/bootstrap/4.5.0/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container">
<H1>update your task!</H1>
<form:form method="POST" commandName="todo">
<fieldset class="form-group">
<form:label path="description">Description:</form:label>
<!-- required validates nulll -->
<form:input path="description" type="text" class="form-control"
required="required" />
<form:errors path="description" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:label path="targetDate">Target Date</form:label>
<form:input path="targetDate" type="Date" class="form-control"
required="required" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<fieldset class="form-group">
<form:radiobutton path="completion" value="true" />
<form:radiobutton path="completion" value="false" />
<form:errors path="targetDate" cssClass="text-warning" />
</fieldset>
<button type="submit" class="btn btn-success">Submit Update</button>
</form:form>
</div>
<script src="webjars/jquery/3.5.1/jquery.min.js"></script>
<script src="webjars/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</body>
</html>
Controller
@Controller
public class ToDoController {
@Autowired
private ToDoService service;
@InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("mm/DD/yyyy");
binder.registerCustomEditor(Date.class, new CustomDateEditor(
dateFormat, false));
}
@RequestMapping(value = "/list-todo", method= RequestMethod.GET)
// HttpSession allows access to the session
public String showToDo(ModelMap model, HttpSession httpSession) {
String user = (String) httpSession.getAttribute("name");
model.addAttribute("todos", service.retrieveTodos(user));
return "list-todos";
}
// redirect to update form
@RequestMapping(value = "/update-todo", method= RequestMethod.GET)
public String getUpdateForm(ModelMap model, @RequestParam int id) {
// To work with command bean
model.addAttribute("todo", service.retrieveTodo(id));
model.clear();
return "updateToDo";
}
@RequestMapping(value = "/update-todo", method= RequestMethod.POST)
public String submitUpdate(ModelMap model, @Valid ToDo todo, BindingResult result) {
if (result.hasErrors()) {
return "redirect:/update-todo";
}
service.updateToDo(todo);
model.clear();
return "redirect:/list-todo";
}
// Will be executed first
@RequestMapping(value = "/add-todo", method= RequestMethod.GET)
public String showAddForm(ModelMap model) {
model.addAttribute("todo", new ToDo());
return "addToDo";
}
/*
* Will be executed after form is submitted
* @Valid ToDo - command bean from addToDo.jsp.
* @Valid to validate the information
* @BindingResult showcases the result of the validation
*/
@RequestMapping(value = "/add-todo", method= RequestMethod.POST)
public String submitAddForm(ModelMap model , @Valid ToDo todo, HttpSession httpSession, BindingResult result) {
System.out.println("running" + result);
// If there is validation error , return to addToDos page for user to fix the error
if (result.hasErrors()) {
return "redirect:/showAddForm";
}
String user = (String) httpSession.getAttribute("name");
service.addTodo(user, todo.getDescription(), todo.getTargetDate(), false);
// Clears the url e.g. name?=jyj123
model.clear();
// return to the url which executes the showToDO
return "redirect:/list-todo";
}
// delete to do entry
@RequestMapping(value = "/delete-todo", method= RequestMethod.GET)
public String deleteToDo(ModelMap model, @RequestParam int id) {
service.deleteTodo(id);
model.clear();
return "redirect:/list-todo"; }
}
Upvotes: 0
Views: 128
Reputation: 2665
In layman's terms this is how Spring MVC works -
Create a bean (generally in GET
handler) to which you want to bind your data. (This bean is referred to as command object)
Put the command object in model
Return a view name. (Spring will resolve the view and send the model to the view)
Bind data (user input) to the command object
Retrieve the command object with data in the controller (POST
or other alike handlers)
Solution to Your Problem:
You are calling model.clear();
in your GET
handler. So, in the view layer, the model is empty and no target bean (i.e. command object) is available to bind data to.
So remove the mode.clear();
call.
NOTE
One other common mistake is to use different names for the key of the command object in the model and in the value of commandName
of <form:form/>
.
i.e., If you put the command object in model as model.put("fooBar", someFooBar);
, in view you need to do <form:form commandName="fooBar" .../>
Further Reading
Upvotes: 0
Reputation: 2643
You are calling model.clear()
, so removed todo
attribute.
public String getUpdateForm(final ModelMap model, @RequestParam final int id) {
// To work with command bean
model.addAttribute("todo", service.retrieveTodo(id));
model.clear();
return "updateToDo";
}
Upvotes: 1