I'm building a blog in Java using Spring and Hibernate. I can't seem to figure out what is going on but I keep running into a Bad Request error when I try to add (save) a post and I can't figure out where I am wrong in my mapping.
Error message:
Controller:
#Controller
#RequestMapping("/blog")
public class IndexController {
#Autowired
private PostService postService;
#RequestMapping("/list")
public String showPage (Model theModel) {
// get posts from DAO
List<Post> thePosts = postService.getAllPosts();
// add the posts to the model
theModel.addAttribute("allPosts", thePosts);
return "allPosts";
}
#GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel) {
//create model attribute to bind form data
Post thePost = new Post();
theModel.addAttribute("post", thePost);
return "postSuccess";
}
#PostMapping("/savePost")
public String savePost(#ModelAttribute("post") Post thePost) {
// save the post using our service
postService.savePost(thePost);
return "allPosts";
}
Form snippet:
<div class="table" id="container">
<form:form action="savePost" modelAttribute="post"
method="POST">
<table>
<tbody>
<tr>
<td><label>Title:</label></td>
<td><form:input path="title" /></td>
</tr>
<tr>
<td><label>Author:</label></td>
<td><form:input path="author" /></td>
</tr>
<tr>
<td><label>Date:</label></td>
<td><form:input path="date" /></td>
</tr>
<tr>
<td><label>Post:</label></td>
<td><form:input path="post" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
<div style="clear: both;"></div>
<p>
Back to Home Page
</p>
</div>
All other pages are working correctly so far, just can't add an actual blog post. Any help is greatly appreciated.
I figured this out and it is similar to another spring issue I had in the past.
I don't think this really follows a lot of conventional function/design theory, but I added some code into the controller and it now works. I can add a post easily.
First thing was, I removed the #ModelAttribute tag from my "savePost" method. Then I added #RequestParam to my method parameters. Added a little bit of logic and now it saves to the database and then appears on the blog. Good stuff.
Code:
#PostMapping("/savePost")
public String savePost(#RequestParam("author") String author,
#RequestParam("title") String title, #RequestParam("date") String date,
#RequestParam("post") String post) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date theDate = sdf.parse(date);
// save the customer using our service
Post thePost = new Post();
thePost.setAuthor(author);
thePost.setDate(theDate);
thePost.setTitle(title);
thePost.setPost(post);
postService.addPost(thePost);
System.out.println(thePost.toString()); //testing
return "success";
}
jsp:
<form:form action="savePost" modelAttribute="post" method="POST">
<table>
<tbody>
<tr>
<td><label>Title:</label></td>
<td><input id="title" type="text" name="title"></td>
</tr>
<tr>
<td><label>Author:</label></td>
<td><input id="author" type="text" name="author"></td>
</tr>
<tr>
<td><label>Date:</label></td>
<td><input id="date" type="text" name="date"></td>
</tr>
<tr>
<td><label>Post:</label></td>
<td><textarea id="post" type="text"
name="post"></textarea></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
Related
I am trying to pass the information from a thymeleaf list and trying to add it to database.
I am getting data from the tmdb and it will be changing so i display the information obtain to the endpoint "/LatestMovies" this information is not saved in the db and ether should it be. so i am trying to add a save button for the custumer to add the movie listed.(its simple it just haves movieid and moviename)
Showing the movies listed i have no problem and it works fine but where i get error is when i add a hidden form. The current code i have is this:
<div class="container">
<table class="table table-hover">
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr th:each="LatestMovies : ${latestMovies}">
<td th:text="${LatestMovies.id}"></td>
<td th:text="${LatestMovies.movieName}"></td>
<td>
<form action="#" th:action="#{/LatestMovies}" th:object="${addMovies}" method="post">
<p><input type="hidden" th:field="*{id}" th:attr="value = ${LatestMovies.id}" /></p>
<p><input type="hidden" th:field="*{movieName}" th:attr="value = ${LatestMovies.movieName}" /></p>
<p><input type="submit" value="Submit" /></p>
</form>
</td>
</tr>
</table>
#Controller
public class LatestMoviesController {
#Autowired
private LatestMoviesDao listOfMovies;
#Autowired
private savedMoviesDao movieRepo;
#GetMapping("/LatestMovies")
public String prueba(Model model) {
TmdbMovies movies = new TmdbApi("22914f477aaa3e7f86c6f5434df8d1eb").getMovies();
ResultsPage<MovieDb> movie = movies.getPopularMovies("en", 1);
for(int i=0; i <= 19; i++){
int movieId = movie.getResults().get(i).getId();
String movieName = movie.getResults().get(i).toString();
listOfMovies.save(new LatestMovies(movieId, movieName));
}
model.addAttribute("latestMovies", listOfMovies.findAll());
return "index";
}
#PostMapping("/LatestMovies")
public String save(#ModelAttribute("addMovies") Model model, SavedMovies addMovies) {
movieRepo.save(addMovies);
return "index";
}
}
Thx in advance
First, let's change your form. You don't need to add a new object to it, since you are already iterating through a list of them. That way, you will also avoid having to add the value for each field manually using th:attr. What we are gonna do, is send the required params separately and then build our movie object with them.
<div class="container">
<table class="table table-hover">
<tr>
<th>Id</th>
<th>Name</th>
</tr>
<tr th:each="LatestMovies : ${latestMovies}">
<td th:text="${LatestMovies.id}"></td>
<td th:text="${LatestMovies.movieName}"></td>
<td>
<form th:action="#{/LatestMovies}" method="post">
<p><input type="hidden" th:value="${LatestMovies.id}" name="id"/></p>
<p><input type="hidden" th:value="${LatestMovies.movieName}" name="name"/></p>
<p><input type="submit" value="Submit"/></p>
</form>
</td>
</tr>
</table>
</div>
Now, on your controller, do the following modifications.
#PostMapping("/LatestMovies")
public String save(#RequestParam("id") Integer id, #RequesParam("name") String name) {
SavedMovies movie = new SavedMovies();
movie.setId(id);
movie.setName(name);
movieRepo.save(movie);
return "index";
}
These changes should do the trick.
I'm trying to make a CRUD application using Spring MVC, but whenever I click the edit button to update an entry in a row, my jsp page to edit an entry (wizardeditform.jsp) has the 'designation' and 'wand' inputs filled in from that entry with the correct values. I would like all the input boxes to have the default values from that row, not just the 'wand' and 'designation' input boxes. Does anyone have any idea why this is happening? (Ive been following this tutorial: http://www.javatpoint.com/spring-mvc-crud-example)
wizardeditform.jsp
<h1>Edit Witch or Wizard</h1>
<form:form method="POST" action="/WizardingWorld/editsave">
<table>
<tr>
<td></td>
<td><form:hidden path="id" /></td>
</tr>
<tr>
<td>First name :</td>
<td><form:input path="firstname"/></td>
</tr>
<tr>
<td>Last name :</td>
<td><form:input path="lastname" /></td>
</tr>
<tr>
<td>Designation :</td>
<td><form:input path="designation"/></td>
</tr>
<tr>
<td>School and house :</td>
<td><form:input path="schoolAndHouse" /></td>
</tr>
<tr>
<td>wand :</td>
<td><form:input path="wand" /></td>
</tr>
<tr>
<td>Blood Type</td>
<td><form:input path="bloodtype" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Save" /></td>
</tr>
</table>
</form:form>
Controller class
// displays form to input data, "command" is request attr. used to display
// object data into form
#RequestMapping("/wizardform")
public ModelAndView showform() {
return new ModelAndView("wizardform", "command", new Wizard());
}
// saves object into database. The #ModelAttribute puts request data into
// model object.
#RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView save(#ModelAttribute("wizard") Wizard wizard) {
dao.save(wizard);
return new ModelAndView("redirect:/viewwizard");// will redirect to
// viewwizard request
// mapping
}
// provides list of employees in model object
#RequestMapping("/viewwizard")
public ModelAndView viewwizard() {
List<Wizard> list = dao.getWizards();
return new ModelAndView("viewwizard", "list", list);
}
// It displays object data into form for the given id. The #PathVariable
// puts URL data into variable
#RequestMapping(value = "/editwizard/{id}")
public ModelAndView edit(#PathVariable int id) {
Wizard wizard = dao.getWizardById(id);
return new ModelAndView("wizardeditform", "command", wizard);
}
// updates model object
#RequestMapping(value = "/editsave", method = RequestMethod.POST)
public ModelAndView editsave(#ModelAttribute("wizard") Wizard wizard) {
dao.update(wizard);
return new ModelAndView("redirect:/viewwizard");
}
The first image is the jsp page to view all the table entries, and the second image is is the wizardeditform.jsp , right after clicking the edit button on the previous page with all the entries.
I'm stuck with my code, I'm trying to pass the info of the object "Student". My scenario is like this:
Registration Form (fill the details then press submit button go to next page)
From this view the model will be printed out then press the next button again.
This third page will just show the information again.
Q: How can i pass the same object and display it to other views?
My code is like this.
Registration view:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Name:</td> <td><input type="text" name="studentName"></td></tr>
<tr>
<td>Age:</td> <td><input type="text" name="studentAge"></td></tr>
<tr>
</table>
<input type="submit" value="submit this">
</form>
First Information View:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
<input type="submit" value="send"/>
</form>
Second Information View:
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
My Controller:
#RequestMapping(value="/stuAdmissionForm.html", method = RequestMethod.GET)
public ModelAndView getStudentAdmissionForm() {
ModelAndView model = new ModelAndView("AdmissionForm");
return model;
}
#RequestMapping(value="/stuSubmitAdmissionForm.html", method = RequestMethod.POST)
public ModelAndView submitModelAttributeAnnotationAdmissionForm(#ModelAttribute("student") Student student) {
ModelAndView model = new ModelAndView("AdmissionSuccess");
return model;
}
#RequestMapping(value="/stuDisplayForm.html", method = RequestMethod.POST)
public ModelAndView getStudent(Student student) {
ModelAndView model = new ModelAndView("NewForm");
model.addObject(student);
return model;
}
In attempting to re-display the information from second view to third view the object Student is not being passed.
There are no fields to submit in your fist information view. You have to add the values to hidden fileds:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
<input type="hidden" name="studentName" value="${student.studentName}">
<input type="hidden" name="studentAge" value="${student.studentAge}">
<input type="submit" value="send"/>
</form>
I asked a question earlier how to do this using Webflow, but it has proven to be impractical for my situation.
I am trying to have a walk through 3 screens which add to an object information and then require a confirm to add to the database at the end. [To maintain simplicity etc]
The first screen takes in username for example
then the next screen requires contact information
then the third screen shows a summary and asks to confirm
I am having trouble figuring out how to pass the same object through several screens. I understand how to pass information from one screen to next, but for some reason same technique doesn’t work through several screens.
Sample 3 pages:
AddUser.jsp
<div id="form">
<h2 >Step 1</h2>
<form action="AddUserContact" method="post">
<table>
<tr>
<td>User Name:</td>
<td><input type="text" id="username" name="username"/></td>
</tr>
<tr>
<td><input type="submit" value="Next"/></td>
</tr>
</table>
</form>
</div>
AddUserContact.jsp
<div id="form">
<h2 >Step 2</h2>
<form action="UserSummaryConfirm" method="post">
<table>
<tr>
<td>${user.username}</td>
</tr>
<tr>
<td>Address:</td>
<td><input type="text" id="address" name="address"/></td>
</tr>
<tr>
<td><input type="submit" value="Next"/></td>
</tr>
</table>
</form>
</div>
UserSummaryConfirm.jsp
<h2>Step 3</h2>
<form action="home" method="post">
<table>
<tr>
<td>User Name:</td>
<td>${user.username}</td>
</tr>
<tr>
<td>Address:</td>
<td>${address}</td>
</tr>
<tr>
<td>Confirm</td>
</tr>
</table>
</form>
I have a Controller for every page [its for me to understand better what is going on, Ill simplify it later]
AddUserController.java
#Controller
public class AddUserController{
#RequestMapping(value = "AddUser")
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
User user = new User();
ModelAndView mav = new ModelAndView("AddUser");
user.setUserName(request.getParameter("username"));
mav.addObject("user", user);
return mav;
}
}
AddUserContactController.java
#Controller
public class AddUserContactController{
#RequestMapping(value = "AddUserContact")
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("AddUserContact");
mav.addObject("user", request.getParameter("user"));
return mav;
}
}
AddUserConfirm.java
#Controller
public class AddUserConfirm{
#RequestMapping(value = "UserSummaryConfirm")
public ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("UserSummaryConfirm");
mav.addObject("user", request.getParameter("user"));
mav.addObject("address", request.getParameter("address"));
return mav;
}
}
And then the User class is just a simple class with getters and setters.
The problem I am having is no matter what I have tried to pass the object, I cannot seem to figure out why the same technique doesnt work.
The address on the third screen is beeing displayed no problem, but the username is not displayed on any of the screens.
With the webflow the way I did it was created a UserBean that was global to all webflow screens. It was easy to add to the same object from any screen and display any information. How can I achieve the same result for this?
Thank you.
WORKING CODE:
Using SessionAttributes
AddUser.jsp
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<div id="form">
<h2 >Step 1</h2>
<form:form action="AddUserContact" commandName="user" method="POST">
<table>
<tr>
<td>User Name:</td>
<td><form:input type="text" id="username" path="username"/></td>
</tr>
<tr>
<td><input type="submit" value="Next"/></td>
</tr>
</table>
</form:form>
</div>
AssUserContact.jsp
<div id="form">
<h2 >Step 2</h2>
<form:form action="UserSummaryConfirm" commandName="user" method="post">
<table>
<tr>
<td>Address:</td>
<td><input type="text" id="address" path="address"/></td>
</tr>
<tr>
<td><input type="submit" value="Next"/></td>
</tr>
</table>
</form>
</div>
UserSummaryConfirm.jsp
<h2>Step 3</h2>
<form:form action="home" method="get">
<table>
<tr>
<td>User Name:</td>
<td><%=session.getAttribute("username")%></td>
</tr>
<tr>
<td>Address:</td>
<td><%=session.getAttribute("address")%></td>
</tr>
<tr>
<td>Confirm</td>
</tr>
</table>
</form:form>
AddUserController.java
#Controller
#SessionAttributes({ "username", "address" })
public class AddUserController{
User usr = new User();
#RequestMapping(value = "AddUser")
public String loadIndex(Model model, User user) {
model.addAttribute("User", user);
return "AddUser";
}
#RequestMapping(value = "AddUserContact")
public String processUserName(Model model, User user) {
usr.setUsername(user.setUsername());
model.addAttribute("User", user);
return "AddUserContact";
}
#RequestMapping(value = "UserSummaryConfirm")
public String processUserContact(Model model, User user) {
usr.setAddress(user.getAddress());
model.addAttribute("username", usr.getUsername());
model.addAttribute("address", usr.getAddress());
return "UserSummaryConfirm";
}
Because in second screen you are using
<tr>
<td>${user.username}</td>
</tr>
User input is not bind to any input so it will not be passed to controller with form data .
While you are using address as
<tr>
<td>Address:</td>
<td><input type="text" id="address" name="address"/></td>
</tr>
As it is bind to input (as you have assigned name="address" which is same as path="name") so its value will be send to controller.
If you want to pass the same object across 3 screens then it's better use #SessionAttribute instead of hiding it and passing again and again.
EDIT :
As you are using session attribute now, remove input element .(which is bind to username) from second screen. Just use instead
<td>session.getAttribute("username")%></td>
I have done a "wizard-style" form like this before. My solution used a single form bean that we put into session for the entire process, and included only the pieces in each page that needs to be added. Spring will not erase a value in the form bean unless you include an input for the given value. We don't use JSR-303, and I don't think this method would work if you are using it. Our validation uses a combination of BindingErrors and custom validation code, so I just setup the code to only validate portions at a time, corresponding to what page the form just submitted.
How to remove some of the "magic value" impression of "command" modelName parameter to create a ModelAndView ?
Example:
#RequestMapping(value = "/page", method = GET)
public ModelAndView render() {
return new ModelAndView("page", "command", new MyObject());
}
One hope was to use a spring constant such as
new ModelAndView("page", DEFAULT_COMMAND_NAME, new MyObject());
I found "command" in the 3 following classes of the spring-webmvc-3.0.5 sources jar:
$ ack-grep 'public.*"command"'
org/springframework/web/servlet/mvc/BaseCommandController.java
140: public static final String DEFAULT_COMMAND_NAME = "command";
org/springframework/web/servlet/mvc/multiaction/MultiActionController.java
137: public static final String DEFAULT_COMMAND_NAME = "command";
org/springframework/web/servlet/tags/form/FormTag.java
56: public static final String DEFAULT_COMMAND_NAME = "command";
The problem is :
BaseCommandController is deprecated
We don't use MultiActionController and FormTag
When you use on your jsp spring tag <form:form>
<form:form method="POST" action="../App/addCar">
<table>
<tr>
<td><form:label path="brand">Name</form:label></td>
<td><form:input path="brand" /></td>
</tr>
<tr>
<td><form:label path="year">Age</form:label></td>
<td><form:input path="year" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</form:form>
you must write:
#RequestMapping(value = "/car", method = RequestMethod.GET)
public ModelAndView car() {
return new ModelAndView("car", "command", new Car());
}
Because the spring framework expects an object with name "command".
Default command name used for binding command objects: "command".
This name to use when binding the instantiated command class to the request.
http://static.springsource.org/spring/docs/1.2.9/api/org/springframework/web/servlet/mvc/BaseCommandController.html
But when you use html form <form> you can write:
#RequestMapping(value = "/car", method = RequestMethod.GET)
public ModelAndView car() {
return new ModelAndView("car", "YOUR_MODEL_NAME", new Car());
}
But on your page
<form method="POST" action="../App/addCar">
<table>
<tr>
<td><form:label path="YOUR_MODEL_NAME.brand">Name</form:label></td>
<td><form:input path="YOUR_MODEL_NAME.brand" /></td>
</tr>
<tr>
<td><form:label path="YOUR_MODEL_NAME.year">Age</form:label></td>
<td><form:input path="YOUR_MODEL_NAME.year" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</form>
I wouldn't use the default name. If the object is a User call it user, if it's Item call it item. If you need a default (for example - for a generic framework), define your own constant.