Spring MVC - Multiple submit button to a Form - java

I am trying to have 2 submit buttons post to a form, with each button action mapped to different controllers. Here are my mappings
#RequestMapping(value="/save", method=RequestMethod.POST, params="save")
#RequestMapping(value="/save", method=RequestMethod.POST, params="renew")
And my submit buttons look like these -
<input type="submit" name="save" class="button" value="Save" />
<input type="submit" name="renew" class="button" value="Renew" />
As you can see from my mapping, I am relying on the use of params to differentiate what button was clicked on. The problem is that it works 90% of the time but sometimes I get the exception below -
java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8090/myapp/save': {public java.lang.String com.myapp.SaveController.save(MyEntity,javax.servlet.http.HttpSession), public java.lang.String com.myapp.SaveController.saveAndRenew(MyEntity,javax.servlet.http.HttpSession)}
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:248)
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:194)
Strangely, when this happens and I re-submit the page, everything works fine afterwards. Is there a better way to achieve what I'm trying to do ?
Thanks!

if the form has these buttons specified:
input type="submit" class="button" name="save" value="Save"
input type="submit" class="button" name="delete" value="Delete"
input type="submit" class="button" name="cancel" value="Cancel"
you may direct to different url request according to button pressed with one controller.
for cancel button,
#RequestMapping(params = "cancel", method = RequestMethod.POST)
public String cancelUpdateUser(HttpServletRequest request) {
return "redirect:/users.html";
}
what request mapping does is to scan post request if it contains params name = cancel.
for save button,
#RequestMapping(params = "save", method = RequestMethod.POST)
public String saveUser(HttpServletRequest request, #ModelAttribute User user, BindingResult result, SessionStatus status) {
// validate your result
// if no errors, save it and redirect to successView.
}

Why not:
<input type="submit" name="action" value="save" />
and then:
#RequestMapping(value="/save", method=RequestMethod.POST)
public String handlePost(#RequestParam String action){
if( action.equals("save") ){
//handle save
}
else if( action.equals("renew") ){
//handle renew
}
}

If You have more controller methods with the same #RequestMapping that differs only in params attribute, You have to explicitly write:
which parameter is supposed to be present in the request, e.g. params="save"
which parameter is NOT supposed to be present in the request, e.g. params="!save"
In Your case:
#RequestMapping(value="/save", method=RequestMethod.POST, params={"save", "!renew"})
#RequestMapping(value="/save", method=RequestMethod.POST, params={"renew", "!save"})
This should fix error Ambiguous handler methods mapped for HTTP path ...
See Spring Web API 4.0.x - RequestMapping#params

Just create one controller with a method similar to this
#RequestMapping(value="/save", method=RequestMethod.POST)
public String handlePost(#RequestParam(required=false , value = "save") String saveFlag , #RequestParam(required=false , value = "renew") String renewFlag){
if(saveFlag != null{
//handle save
}
else if(renewFlag !=null{
//handle renew
}
}

One more solution:
#RequestMapping(value="/save", method={RequestMethod.POST}, params={"save=Save"})
#RequestMapping(value="/save", method={RequestMethod.POST}, params={"renew=Renew"})

Related

Method Not Allowed, status=405, HTML form using Thymeleaf

I made a Form using Thymeleaf but running into this issue.
I read many articles but didn't find any solution.
Any solution you can suggest ?
Project Controller ->
#Controller
public class Controllers {
#GetMapping("/home")
public ModelAndView home(){
System.out.println("User is in Homepage");
return new ModelAndView("index");
}
#GetMapping("/service")
public ModelAndView service(){
System.out.println("User is in Service Page");
return new ModelAndView("service");
}
#GetMapping("/about")
public ModelAndView about(){
System.out.println("User is in About page");
return new ModelAndView("about");
}
Here's Controller Class for submitting form ->
#Controller
public class SavingUser{
#Autowired
private UserRepository userRepository;
#PostMapping("/registerUser")
public ModelAndView user(#ModelAttribute Customer customer, ModelMap model){
System.out.println("User in registration page..");
userRepository.save(customer);
model.addAttribute("saveUser", customer);
return new ModelAndView("index");
}
}
And here's my HTML Form -
<div id="form">
<form action="registerUser" th:action="#{/registerUser}" th:object="${saveUser}" method="POST">
<br />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<label for="name">Your Name:</label><br />
<input type="text" th:field="*{name}" placeholder="" /><br />
<label for="suburb">Your Suburb</label><br />
<input type="text" th:field="*{suburb}" placeholder="" /><br />
<input class="submit" type="submit" value="Submit" />
<br /><br />
</div>
</form>
</div>
I tried to remove action="", still it didn't work.
Well, from what I see in the code you miss the #GetMapping method in your controller to actually display the page. Not very clear in what step you get 405 status. It would be useful if you also add the relevant exception message from the console.
Edit:
to answer the original question, you get 405 because in the Post Controller you make a POST request to "/services" which dosen't exists (only the GET exists for services).
#PostMapping("/registerUser")
public ModelAndView user(#Valid #ModelAttribute Customer customer, BindingResult result, ModelMap model){
[...]
return new ModelAndView("service"); // this makes a POST to "service" endpoint!
}
To correct that, you must make a redirect to the page like this:
#PostMapping("/registerUser")
public ModelAndView user(#Valid #ModelAttribute Customer customer, BindingResult result, ModelMap model){
[...]
return new ModelAndView("redirect:/service"); // this makes a GET to "service" endpoint
}
Leaving that aside, there are many things that can be imporoved. First of all, you are not using Thymeleaf in your project. No Thymeleaf markup will be processed. To use it you must add first the dependency, then configure Thymeleaf as your HTML resolver. The proper way to do all that is detailed here.
Also, I really recommend reading Thymeleaf documentation and follow a few tutorials to understand how things work.

How can I call a method from the controller into thymeleaf without changing the URL

I have a html page with Thymeleaf and i have a button in that page. That button it is calling a method from the controller that i have mapped with /addService but it return the /index page(Because I'm already in that page and i want to remain in it). Therefore, when i press the button it is redirecting me to the index page but the URL is changed with /addService. I would like to remain in the index page without the /assService in the URL. Below my code of the form and of the controller. How can I avoid this behaviour?
Thanks in advance.
My index.html
<form action="#" th:object="${serviceOffered}" th:action="#{addService}" method="POST">
<input type="hidden" th:value="${serviceOffered.name}" name="name"/>
<button type="submit" class="btn">
<i class="fas fa-plus"></i>
</button>
</form>
My controller
#RequestMapping(value="addService", method = RequestMethod.GET)
public ModelAndView getServiceSelected() {
ModelAndView modelAndView = new ModelAndView();
ServiceOffered serviceOffered = new ServiceOffered();
modelAndView.addObject("serviceOffered", serviceOffered);
modelAndView.setViewName("/index");
return modelAndView;
}
Use redirect
modelAndView.setViewName("redirect:/");
And define your url "/"
#GetMapping("/")
public String indexPage() {
return "index":
}
Assuming that you use Spring MVC you should change your RequestMethod to POST, because you're POSTing a form. Otherwise the request is not handled correctly.

Reset a controller with another in Spring

I'm a trainee java-dev and this is my first question here so please, don't judge me!
I have a Controller class which works with jsp files. The first jsp (userinput.jsp) has 3 text fields (lat, lon, radius) and 2 buttons (submit, apply default values). The second jsp is just an HTML table filled with data (depends on user input), and a reset button which should return you to the starter page (userinput.jsp) and delete all existing data. How should I do this?
Bonus question: If I try to refresh the page at the second state (html table), the browser generates a warning that says I'll lose all data and I shouldn't refresh. How can I get rid of this?
#Controller
#EnableAutoConfiguration
class SpringBootController implements InitLogger {
#GetMapping(value="/geohash")
public String getUserInput(ModelMap model) {
model.put("command", new Tuple());
return "UserInput";
}
#PostMapping(value="/geohash", params="SubmitWithDefault")
public String defaultUserInput(ModelMap model) {
model.put("command", tupleFill (48.104564, 20.800041, 6) );
return "UserInput";
}
#PostMapping(value = "/geohash", params="Submit")
public String printHash(#ModelAttribute("user")Tuple tuple,ModelMap model) {
GetData.setLat1(tuple.getFirstCoordinate());
GetData.setLon1(tuple.getSecondCoordinate());
GetData.setRad1(tuple.getRadius());
LocationExecute.calculate();
model.addAttribute("geoItemList", LocationExecute.getTupleList());
model.addAttribute("listSize", LocationExecute.getTupleList().size());
return "Geohash";
}
#PostMapping(value = "/geohash", params="reset", method = RequestMethod.GET)
public ModelAndView method() {
return new ModelAndView("redirect:geohash");
}
}
userinput.jsp - buttons
<input type="submit" name="Submit" value="Submit" style="height:25px; width:100px"/>
<input type="submit" name="SubmitWithDefault" value="Default Values" style="height:25px; width:100px">
geohash.jsp - (html table) reset button
<input type="reset" name="reset" value="Reset" style="height:30px; width:100px">
You need to implement PRG(Post -Redirect-Get) design pattern in MVC to solve the issue.
Please go through the below for more information
http://www.c-sharpcorner.com/UploadFile/dacca2/implement-prg-pattern-in-mvc-architecture/
So instead of returning view name redirect it so your problem will be solved, if you want some data to be sent to redirect method then use flashAttributes of spring
Solved it this way:
geohash.jsp
(added form:form tags)
<form:form action="/geohash">
<th> <input type="submit" name="reset" value="Reset" style="height:30px; width:100px"> </th>
</form:form>
Controller
(changed method to post)
#RequestMapping(value = "/geohash", params="reset", method = RequestMethod.POST)
public ModelAndView method() {
return new ModelAndView("redirect:geohash");
}

Thymeleaf multiple submit button in one form

I have a fragment of HTML page with one form and 2 button:
<form action="#" data-th-action="#{/action/edit}" data-th-object="${model}" method="post">
<button type="submit" name="action" value="save">save</button>
<button type="submit" name="action" value="cancel">cancel</button>
</form>
And the controller:
#RequestMapping(value="/edit", method=RequestMethod.POST)
public ModelAndView edit(#ModelAttribute SomeModel model,
#RequestParam(value="action", required=true) String action) {
if (action.equals("save")) {
// do something here
}
if (action.equals("cancel")) {
// do another thing
}
return modelAndView;
}
This work good, but if I have more button, I must add more if statement to check the action string. Is there another way that I can create one action for each button in the form?
You can create separate methods with different #RequestMappings using the params variable.
#RequestMapping(value="/edit", method=RequestMethod.POST, params="action=save")
public ModelAndView save() {}
#RequestMapping(value="/edit", method=RequestMethod.POST, params="action=cancel")
public ModelAndView cancel() {}
this works in my problem.
use th:formaction on submit button this is work on how many you have submit button
and this is also usefull for give more links to one form with different submit button
<form action="#" class="form" th:action="#{'/publish-post/'+${post.id}}" method="post">
<input class="savebtn" type="submit" value="Save" th:formaction="'/save-post/'+${post.id}">
<input class="publish" type="submit" value="Publish Article">
</form>
Instead of an if-case you could have a switch case, should you not want to take in every option as a new request mapping.
#RequestMapping(value="/edit", method=RequestMethod.POST)
public ModelAndView edit(#ModelAttribute SomeModel model,
#RequestParam(value="action", required=true) String action) {
switch(action) {
case "save":
// do stuff
break;
case "cancel":
// do stuff
break;
case "newthing":
// do stuff
break;
default:
// do stuff
break;
}
}
For multiple submit buttons, the below worked for me.
Notice the: th:formaction in the cancel button.
<form action="#" th:action="#{/saveProducts}" th:object="${model}" method="post">
<button type="submit" name="action" value="cancel" th:formaction="#{/cancelProducts}">CANCEL</button>
<button type="submit" name="action" value="save">SAVE</button>
</form>
For the controller:
#RequestMapping(value="/saveProducts", method= RequestMethod.POST)
public String save(#ModelAttribute SomeModel model) {
*//Do something*
}
#RequestMapping(value="/cancelProducts", method=RequestMethod.POST)
public String cancel(#ModelAttribute SomeModel model) {
*//Do something*
}
You can know which submit button has been clicked and then act upon the button
Here is the code
String btnName = request.getParameter("action");
if(btnName.equals("save"))
// you code....
else if(btnName.equals("cancel"))
// you code....

Unable to Access Request Parameter in SpringMVC Controller while redirecting to URL

I am having a form which submits new articles to my controller.
JSP Page:
<form class="form-signin" method="post"
action="/articleViewer">
<div class="control-group" style="margin-top: -5px;">
<label class="control-label text-info" for="commentContent"><strong>Post
Comment</strong></label>
<div class="controls">
<textarea class="FormElement" name="area2" id="commentContent"
style="width: 100%;"></textarea>
</div>
</div>
<button type="submit" class="btn btn-primary" style="margin-left: 90%;">Post</button>
</form>
Controller Method:
#RequestMapping(value="/articleViewer", method = RequestMethod.POST)
public String saveArticleComment (HttpServletRequest request, HttpServletResponse response,Principal principal, ModelMap map) throws ServletException, IOException{
//processing request
System.out.println("Link : "+Path.Jsp.ARTICLE_VIEWER_PAGE);
return Path.Jsp.ARTICLE_VIEWER_PAGE; //this ARTICLE_VIEWER_PAGE = /articleViewer
}
Now from the above controller method I wanna redirect to another method where I want to pass
currently saved article id as http://<myurl>/article?articleId="xyz".
Here is my get method code for handling the redirect.
#RequestMapping(value="/articleViewer", method= RequestMethod.GET)
public String articleViewer(HttpServletRequest request, Principal principal,
ModelMap map, HttpServletResponse response)
throws DatabaseException {
//I wanna access article id here.
return Path.Jsp.ARTICLE_VIEWER_PAGE;
}
I wanna know how could I access that passed request parameter in above method?
If you submit the action url without parameter, or use hidden field for this purpose then you should return that parameter back as a result. So, you don't get it lost, or redirect to the page where the parameter is not needed anymore. To pass parameter in the url use
<form class="form-signin" method="post" action="/articleViewer?varArticleID=94">
I Resolved it by using Redirect attribute in return ...
return "redirect:/articleViewer?varArticleID="+getVarArticleID();
So if you need the page like as after submitting change the action value of form like
<form class="form-signin" method="post"
action="/articleViewer?varArticleID={yourArticleid}">
Also after submit you need
return Path.Jsp.ARTICLE_VIEWER_PAGE+"?varArticleID="+request.getParameter("varArticleID");

Categories

Resources