AJAX jQuery CRUD with spring mvc without repeating RequestMethods - ajax

i have a page with a form and i have a AJAX function that returns a json with user details to fill some fields of that form and also in the same page i have a button to create a user from the information of that form
my problem is that to do this i have 3 controllers of the same page like this
this controllers returns the form with a GET method
#Controller
#RequestMapping("forms")
public class myController {
#RequestMapping(value = "myForm", method = RequestMethod.GET)
public String getPage (Model model) {
return "forms/myForm";
}
this controller is the one that gets me the some of the data in the form
RequestMapping(value = "myForm", method = RequestMethod.POST)
public #ResponseBody String searchSomeFields(#RequestBody final String json, Model model)
throws IOException
{
ObjectMapper mapper = new ObjectMapper();
User objectMap = mapper.readValue(json, User.class);
//get some data a fill some fields in the form
return toJsonInsertar(objectMap);
}
and in this controller i do the insert
RequestMapping(value = "myForm", method = RequestMethod.POST)
public #ResponseBody boolean insertUser(#RequestBody final String json, Model model)
throws IOException
{
ObjectMapper mapper = new ObjectMapper();
User objectMap = mapper.readValue(json, User.class);
//insert the user with all the data in the form
return toJsonInsertar(objectMap);
}
i try using two GET in the firts two controllers but i get a error saying that i already have a controller with the same requestMethod and value, i try puting PUT in the third controller that does the insert and it worked, but i read that PUT is used to do EDITS.
how can i do this insert with what i have ?

Related

Neither BindingResult nor plain target object for bean name AFTER redirect

I have a form like this (in a page called add.jsp):
<form:form action="${pageContext.request.contextPath}/add" method="post" modelAttribute="addForm">
</form:form>
On GET request, i populate modelAttribute:
#RequestMapping(value ="add", method = RequestMethod.GET)
public ModelAndView add(Map<String, Object> model) {
model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
When i perform the form submitting (a POST request), i have the follow method:
#RequestMapping(value ="add", method = RequestMethod.POST)
public ModelAndView add(Map<String, Object> model, #Valid AddUserForm form, Errors errors) {
if (errors.hasErrors()) {
//model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
....
}
But i get this error: Neither BindingResult nor plain target object for bean name 'addForm' available as request attribute
My workaround is to add model.put("addForm", new AddUserForm());, the command that i have commented on POST request.... but... where is my error ?
In both case, you are returning the same view (i.e. "add") and this view contains a form with a modelAttribute="addForm" therefore the model MUST contains an object named "addForm".
If you don't wan't to populate your model with a new AddUserForm after a POST with errors, you probably should :
return another view (without the "addForm" model attribute)
or
reuse the same "addForm": model.put("addForm", form);

Spring #ExceptionHandler, how to preserve model attributes

Seems like #ExceptionHandler clears the model populated by the request handler that threw the exception. Consider the following scenario:
#Controller
public class GreetController {
#RequestMapping(value = "form", method = RequestMethod.GET)
public String showForm(#ModelAttribute("userInfoFormObject") UserInfoForm form) {
return "form";
}
#RequestMapping(value = "processform", method = RequestMethod.POST)
public String processForm(#Valid #ModelAttribute("userInfoFormObject") UserInfoForm form,
BindingResult errors)
throws RegisterFormException {
if (errors.hasErrors())
throw new RegisterFormException();
return "greet";
}
#ExceptionHandler(RegisterFormException.class)
public String registerFormException() {
return "form";
}
}
User inputs invalid data into a register form, RegisterFormException is thrown and exception handler takes user back to register form. Spring jstl tag library expects UserInfoForm object as an model attribute. However, exception handler creates new empty model. Is there way to preserve the populated model across exception handler, or is my only choice to return form view name in the request handler in the case of errors? Is the example solution considered as an anti pattern?

Spring binding multiple attributes to same #ModelAttribute

I have a preview page which takes add or edit models and displays the preview.
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
I need to call preview from add form page and edit form page. The models I'm going to pass are same but come from different forms.
(1) Is there a way ModelAttribute supports this kind of multi-attribute name mapping?
(2) How can I think about redesigning this? Thinking about (a) Renaming the form name/attribute before form submit to use the same attribute name. (b) Remove ModelAttribute altogether - That's not an option for me as I'm using spring mvc form binding.
Note: I'm using editForm/addForm as session attributes.
Not actually solutions but work arounds.
Approach 1:
#RequestMapping(value = "/preview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
Approach 2:
#RequestMapping(value = "/editpreview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/addpreview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}

#ModelAttribute in Controller does not auto increment ID

I am working on Spring web based project and I am having trouble with #ModelAttribute that return model object to JSP file to be filled then it will be passed to controller function then data will be saved to database. Let me show you some code.
It is my Software Engineering Course Project for more detailed information code is available on github:
https://github.com/IYTECENG316SoftwareEngineering/reddit
#Controller
public class MessageController {
#ModelAttribute("privateMessage")
public PrivateMessage constructPrivateMessage() {
return new PrivateMessage();
}
#RequestMapping(value = "/message/{id}", method = RequestMethod.POST, params = "sendMessage")
public String doSendMessage(Model model, #PathVariable("id") int id,
#Valid #ModelAttribute("privateMessage") PrivateMessage privateMessage, BindingResult result,Principal principal) {
if (result.hasErrors()) {
return showMessage(model,id);
}
User messageOwner = userService.findOne(principal.getName());
//I need to create new instance of PrivateMessage because
//(#ModelAttribute("privateMessage") PrivateMessage privateMessage) this gives always same ID.
PrivateMessage message = new PrivateMessage();
message.setMessage(privateMessage.getMessage());
message.setUser(messageOwner);
PrivateMessageConversation conversation = messageService.findOneWithMessages(id);
message.setPrivateMessageConversation(conversation);
messageService.save(message);
return "redirect:/message/"+message.getID()+".html";
}
}
PrivateMessage object send to jsp file and it filled send back to doSendMessage function with #ModelAttribute. Object come with filled (all the inputs written in to object perfectly) but only problem is that its ID is not auto-incremented. There is one more code that I want to show. We use same template for topic and it works perfectly.Here the code;
#Controller
public class UserController {
#ModelAttribute("topic")
public Topic contructTopic() {
return new Topic();
}
#ModelAttribute("entry")
public Entry contructEntry() {
return new Entry();
}
#RequestMapping(value = "/account", method = RequestMethod.POST)
public String doAddNewTopic(Model model,
#Valid #ModelAttribute("topic") Topic topic,
BindingResult resultTopic, Principal principal,
#Valid #ModelAttribute("entry") Entry entry,
BindingResult resultEntry,
#RequestParam("topic_category") String category_string) {
System.out.println(principal.getName() + " " + category_string + " "
+ topic.getTitle() + " " + entry.getDescription());
if (resultTopic.hasErrors()) {
return account(model, principal);
}
if (resultEntry.hasErrors()) {
return account(model, principal);
}
String name = principal.getName();
Category category = categoryService.findByName(category_string);
topic.setCategory(category);
topicService.save(topic);
entry.setTopic(topic);
entry.setPublishedDate(new LocalDateTime());
entryService.save(entry, name);
return "redirect:/topic/" + topic.getId() + ".html";
}
}
Above code work perfectly. Topic and entry object send to jsp, they filled and send back to controller and all their attributes fine and IDs are auto-incremented. We could not figure auto why first one is not working.
NOTE: We are using Hibernate, Spring Data JPA and Tiles
In your first controller (MessageController) you are constructing a new instance of PrivateMessage and saving that. The hibernate generated id will be changed there. Then you are doing a redirect with a path pattern (redirect:/message/{id}.html). The pattern will be expanded with the original id the method doSendMessage has been called with.
In your second (working) controller you are not creating a new instance of Topic, so after saving topic the hibernate assigned id is contained in your topic. After that you are also not using springs path expansion but constructing a path by hand using the new id.
Change your first controller from
return "redirect:/message/{id}.html";
to
return "redirect:/message/" + message.getId() + ".html";
or
return UriComponentsBuilder.fromUriString("redirect:/message/{id}.html")
.buildAndExpand(message.getId())
.encode()
.toUriString()

Spring Framework 3 and session attributes

I have form object that I set to request in GET request handler in my Spring controller. First time user enters to page, a new form object should be made and set to request. If user sends form, then form object is populated from request and now form object has all user givern attributes. Then form is validated and if validation is ok, then form is saved to database. If form is not validated, I want to save form object to session and then redirect to GET request handling page. When request is redirected to GET handler, then it should check if session contains form object.
I have figured out that there is #SessionAttributes("form") annotation in Spring, but for some reason following doesnt work, because at first time, session attribute form is null and it gives error:
org.springframework.web.HttpSessionRequiredException: Session attribute 'form' required - not found in session
Here is my controller:
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
FormUtils.populate(form, request);
if(form.validate())
{
formDao.save();
}
else
{
return viewForm(form);
}
return null;
}
It throws Exception if controller called first time even though added #SessionAttributes({"form"}) to class. So add following populateForm method will fix this.
#SessionAttributes({"form"})
#Controller
public class MyController {
#ModelAttribute("form")
public Form populateForm() {
return new Form(); // populates form for the first time if its null
}
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
The job of #SessionAttribute is to bind an existing model object to the session. If it doesn't yet exist, you need to define it. It's unnecessarily confusing, in my opinion, but try something like this:
#SessionAttributes({"form"})
#Controller
public class MyController {
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
Note that the #SessionAttributes is declared on the class, rather than the method. You can put wherever you like, really, but I think it makes more sense on the class.
The documentation on this could be much clearer, in my opinion.
if there is no defined session object so I think it's gonna be like this:
#SessionAttributes({"form"})
#Controller
public class MyController {
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm() {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
#Controller
#SessionAttributes("goal")
public class GoalController {
#RequestMapping(value = "/addGoal", method = RequestMethod.GET)
public String addGoal(Model model) {
model.addAttribute("goal", new Goal(11));
return "addGoal";
}
#RequestMapping(value = "/addGoal", method = RequestMethod.POST)
public String addGoalMinutes(#ModelAttribute("goal") Goal goal) {
System.out.println("goal minutes " + goal.getMinutes());
return "addMinutes";
}
}
On page addGoal.jsp user enters any amount and submits page. Posted amount is stored in HTTP Session because of
#ModelAttribute("goal") Goal goal
and
#SessionAttributes("goal")
Without #ModelAttribute("goal") amount entered by user on addGoal page would be lost
I'm struggling with this as well. I read this post and it made some things clearer:
Set session variable spring mvc 3
As far as I understood it this basically says:
that Spring puts the objects specified by #SessionAttributes into the session only for the duration between the first GET request and the POST request that comes after it. After that the object is removed from the session. I tried it in a small application and it approved the statement.
So if you want to have objects that last longer throughout multiple GET and POST requests you will have to add them manually to the HttpSession, as usual.

Resources