I have a table for adding one object to another and deleting one object from another.
This is my controller:
#Controller
#RequestMapping("/proj/{pid}/coupling/{r1}")
public class CouplingController {
#RequestMapping(method = RequestMethod.GET)
public String getAllCouplings( ){
return "riskCoupling";
}
#RequestMapping(value = "/{r1}", method = RequestMethod.POST)
public String saveCoupling( ){
return "/projects";
}
#RequestMapping(value = "/{r2}", method = RequestMethod.DELETE)
public String removeCoupling(){
return "/projects";
}
}
This is my Thymeleaf view
<td>
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "post">
<input type="submit" name="Couple" value="Couple" class="btn btn-info" />
</form>
</td>
<td">
<form action = "#" th:action="#{/proj/{pid}/coupling/{r1}/{r2} (pid=${projectID},r1=${r1ID},r2=${r2.id})}" method = "delete">
<input type="submit" name="RemoveCoupling" value="RemoveCoupling" class="btn btn-info" />
</form>
</td>
When I go to the url /proj/{pid}/coupling/{r1} I get the overview so the GET works.
When I press the Couple button it works. So the POST works.
When I press the RemoveCoupling button I get the error:
Request method "Get" not supported.
I really don't know why I get this error.
Since browsers unfortunately do not support any other verbs than POST and GET in HTML forms, they will just send POST/GET requests instead. This might be the reason why only that one does not work.
See more:
http://martinfowler.com/articles/richardsonMaturityModel.html
http://amundsen.com/examples/put-delete-forms/
http://www.quora.com/HTTP/Why-are-PUT-and-DELETE-no-longer-supported-in-HTML5-forms
I fixed this with Thymeleaf - Button click to call http delete method.
Basically putting a hidden field in my form.
Related
I have a data base which contains some items. I want to create a form which edits item with some id. I did it, form opens fine. Adress is /itemproject/edit_item/{id} Problems start when I'm trying to activate POST method. Instead of directing me to page with item list (/itemproject/view_items) programm sends me to /itemproject/edit_item/edit_item. itemproject is context path (for example).
#RequestMapping(value = "/edit_item/{id}", method = RequestMethod.GET)
public String editItem(#PathVariable("id") Integer id, Model model) {
Item item;
item = dbService.findItem(item).get(0);
model.addAttribute("item", item);
return "edit_item";
}
#RequestMapping(value = "/edit_item/{id}", method = RequestMethod.POST)
public String editItemComplete(#PathVariable("id") Integer id, #ModelAttribute("item") Item item, Model model) {
dbService.updateItem(item);
model.addAttribute("items",dbService.findAllItems());
return "view_items";
}
dbService works with data base.
I want that programm sent me to list of all items after ediding chosen item and updating it in database.
Here is example of edit form (url: /itemproject/edit_item/{id}
<spring:url value="edit_item" var="formURL"/>
<form:form action="${formURL}"
method="post" cssClass="col-md-8 col-md-offset-2"
modelAttribute="item"
>
<div class="form-group">
<label for="item-stuff">Stuff</label>
<form:input id="item-stuff"
cssClass="form-control"
path="stuff"/>
</div>
<button type="submit" class="btn btn-default">Edit item</button>
</form:form>
This is how my item list page looks (url: /itemproject/view_items)
<body>
<table class="table table-hover">
<tbody>
<tr>
<th>Stuff</th>
</tr>
<c:forEach items="${items}" var="item">
<tr>
<td>${item.stuff}</td>
</tr>
</c:forEach>
</tbody>
</table>
</body>
From Spring docs:
In Spring MVC you can use the #PathVariable annotation on a method
argument to bind it to the value of a URI template variable
That means that #PathVariable annotation is suitable when you use the GET method because when you use GET method you can pass your query string.
Instead, try to use #RequestBody in order to try to bind your POST HTTP body message to your parameter
For example:
#RequestMapping(value = "/edit_item", method = RequestMethod.POST)
public String editItemComplete(#RequestBody String body) {
//in here you'll have to pull the body content
return "view_items";
}
Let's say that you're sending an Integer id on HTTP POST body, then you can pull the data from the body like this:
#RequestMapping(value = "/edit_item", method = RequestMethod.POST)
public String editItemComplete(#RequestBody String body) {
ObjectMapper objectMapper = new ObjectMapper();
try {
idJson = objectMapper.readTree(body).path("id").asInt();
} catch (IOException e) {
e.printStackTrace();
}
return "view_items";
}
assuming that you're sending json from client to service.
Rather than loading the items and returning the view_items template, you can return "redirect:/itemproject/view_items" and that will cause your handler for view_items to be invoked, which will load the items etc.
I am still relatively new to springMVC, I currently have some code, that will take an input from a <form:input> on a jsp page, and create different lists depending on the input(providing I enter the correct String). I would like to transition this into a button instead, so I could have four buttons that will return a different String, for example "one", "two", "three" and "four". That way there is no typing needed from the user.
I see that there is a <form:button> available but I do not know how I could return the String value from this. Also I have looked into angularJS and seen that you can call a function onClick. But again, I don't know what the implementation would have to be to tie it into my Controller. I am just not really sure how I can implement this. Any help would be appreciated.
This is what I have being implemented at the moment :
<form:form commandName="input">
<label>Enter Value</label>
<form:input path="listType" class="inputbox" />
<br>
<input type="submit" class="button" value="Enter" />
</form:form>
This takes the input and stores it in an object :
#Controller
#SessionAttributes("input")
public class EventController {
#RequestMapping(value = "/event", method= RequestMethod.GET)
public String displayEvent (Model model) {
AccessInput userInput = new AccessInput();
model.addAttribute("input", userInput);
System.out.println("finished get method");
return "event";
}
#RequestMapping(value = "/event", method= RequestMethod.POST)
public String processEvent(#ModelAttribute("input")AccessInput userInput) {
System.out.println(userInput.getListType()); //just so I know what value it has
return "redirect:results.html";
}
This is the controller that creates my list based on the string that I pass through to the object
#RestController
#SessionAttributes("input")
public class ReportController {
#RequestMapping(value="/events")
public List<Appliance> getEvents(#ModelAttribute("input")AccessInput userInput) {
List<Appliance> events = new ArrayList<>();
events = ProcessChoice.ofList(userInput.getListType());
System.out.println(userInput.getListType());
return events;
}
}
Edit:
Just to note I have resolved this, I followed the example given by Vipin Dubey, I had to change my controller. I removed the POST method and added in a #RequestParam as a parameter, and redirected the buttons on the event.jsp to "results.html?input=one" then took this value and added it to my model to store it in the session.
#Controller
#SessionAttributes("URLparam")
public class ResultController {
#RequestMapping(value = "/results.html", method = RequestMethod.GET)
public String buttonSelect(Model model, #RequestParam("input")String input) {
model.addAttribute("URLparam", input);
System.out.println(input);
return "result";
}
}
You have two options :
1. Short and recommended way :
Use a link and style it as a button and you can directly call your controller
<a class="btn" href="/events?input=one">One</a>
<a class="btn" href="/events?input=two">two</a>
<a class="btn" href="/events?input=three">three</a>
<a class="btn" href="/events?input=four">four</a>
2. Use jQuery or JavaScript to submit the form based on clicked button using a hidden input field in your form
<!-- Buttons with classes -->
<div id="target">
<button class="one">One</button>
<button class="two">two</button>
<button class="three">three</button>
<button class="four">four</button>
</div>
<!-- Your form -->
<form:form commandName="input" id="myForm">
<input type="hidden" name="inputbox" id="inputbox" value=""/>
</form:form>
// You will have to do this for each of the button which is not a recommended way
$( ".one" ).click(function() {
$('input[name="inputbox"]').val("one");
//var a = $('input[name="inputbox"]').val();
//alert(a);
$( "#myForm" ).submit();
});
I am trying to get a value from thymeleaf input into my java class.
Simple script from thymeleaf
<h1>Form</h1>
<form action="#" th:action="#{index}" th:object="${emails}" method="post">
<p>Emails: <input id="emailbox" type="text" th:field="*{email}"
placeholder="Enter e-mails: eg; Foo#gmail.com, Bar#yahoo.com"/></p>
<p><input type="submit" value="Submit"/> <input type="reset" value="Reset"/></p>
</form>
How would I be able to retrieve the inputted text into my java class?
Controller
#Controller
public class IndexController {
#RequestMapping(value = "/index", method = RequestMethod.GET)
public ModelAndView getdata() throws IOException {
ModelAndView model = new ModelAndView("index");
model.addObject("emails", new MailModel());
return model;
}
#PostMapping("/index")
public String emailSubmit(#ModelAttribute MailModel emails) {
System.out.println(emails.getEmail());
return "index";
}
I am able to run my application and see thymeleaf view. When I enter some text into the text box and hit submit. I receive an error.
Error Message
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'emails' available as request attribute
My Application is created with Springboot, Java, and Thymeleaf. What am I doing wrong? Is it possible that ModelandView does not work with PostMapping? I also followed https://spring.io/guides/gs/handling-form-submission/ and I got that sample working, but when I tried to follow the logic and implement into my project. It did not work.
In your HTML, change th:action="#{index}" to th:action="#{/index}". This will allow Thymeleaf to resolve it properly.
By using Spring MVC + Maven + Hibernate I'm trying to build a Spring MVC form that has both an "Add To Cart" button, as well as a "Add to Favourites" button. Whenever one button is pressed, the other one returns null as shown in the code below.
Both buttons are type=submit and I'm treating them in the code below. The problem I'm having is that the "Add to Favourites" case is always resulting into an uncaught exception (redirecting me to the "Internal Error" page).
I'm wondering if maybe there's an elegant way to treat each button press in a form separately, without having lots of if - statements for each button.
The HTML file has the following items declared in the form's structure:
<c:if test="${empty readOnly}">
<input type="number" value="1" name="quantity" class="input-mini"/>
<button class="btn btn-primary" type="submit" name="addToCart"><i class="icon-shopping-cart icon-white"><jsp:text /></i> Add</button>
<button class="btn btn-secondary" type="submit" name="favourite"><jsp:text /> Favourite This!</button>
</c:if>
And I'm using the following Controller method:
#RequestMapping(value = "/submit", method = RequestMethod.POST, produces = "text/html")
public String submit(HttpServletRequest request, Model uiModel) {
init(uiModel);
String id = request.getParameter("product-id");
String quantity = request.getParameter("quantity");
if(!request.getParameter("addToCart").toString().equals(null))
{
if (StringUtils.isNotBlank(id) && StringUtils.isNotBlank(quantity)) {
shoppingCartServiceLocal.addToShoppingCart(shoppingCart, id, quantity);
}
}
if(!request.getParameter("favourite").equals(null))
{
//ADD TO FAVOURITE
}
return "redirect:/items/" + id;
}
I might be missing something obvious as I'm still learning these. Any help would be greatly appreciated.
Use the param attribute to specify which method to use for which button.
#RequestMapping(value = "/submit", method = RequestMethod.POST, params = "addToCart")
public String addToCart(HttpServletRequest request, Model uiModel) {
//your code
}
#RequestMapping(value = "/submit", method = RequestMethod.POST, params = "favourite")
public String addToFavorites(HttpServletRequest request, Model uiModel) {
//your code
}
Im getting this error: HTTP Status 405 - Request method 'POST' not supported
What I am trying to do is make a form with a drop down box that get populated based on the other value selected in another drop down box. For example when I select a name in the customerName box the onChange function in the .jsp page should be run and the page submitted then loaded again with the corresponding values in the customerCountry box.
however I'm getting this HTTP Status 405 error. I have searched the internet for a solution but haven't been able to find anything that helped. Here is the relevant parts of my code:
part of jsp page
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
.error { color: red; }
</style>
<script>
function repopulate(){
document.deliveryForm.submit();
}
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
// document.submitForm.submit(); (This was causing the error)
}
</script>
</head>
<body>
<h1>Create New Delivery</h1>
<c:url var="saveUrl" value="/test/delivery/add" />
<form:form modelAttribute="deliveryDtoAttribute" method="POST" action="${saveUrl}" name="deliveryForm">
<table>
<tr>
<td><form:hidden id="hasId" path="hasCustomerName" value="true"/></td>
</tr>
<tr>
<td>Customer Name</td>
<td><form:select path="customerName" onChange="repopulate()">
<form:option value="" label="--- Select ---" />
<form:options items="${customerNameList}" />
</form:select>
</td>
<td><form:errors path="customerName" cssClass="error" /></td>
</tr>
<tr>
<td>Customer Country</td>
<td><form:select path="customerCountry">
<form:option value="" label="--- Select ---" />
<form:options items="${customerCountryList}" />
</form:select>
</td>
<td><form:errors path="customerCountry" cssClass="error" /></td>
</tr>
</form:form>
<form:form name="submitForm">
<input type="button" value="Save" onClick="setFalse()"/>
</form:form>
</body>
</html>
part of controller:
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String getDelivery(ModelMap model) {
DeliveryDto deliveryDto = new DeliveryDto();
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// I want to enter this method if hasId=true which means that a value in the CustomerName
// drop down list was selected. This should set the CountryList to the corresponding values
// from the database. I want this post method to be triggered by the onChange in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=true")
public String postDelivery(
#ModelAttribute("deliveryDtoAttribute") DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
}
// This next post method should only be entered if the save button is hit in the jsp page
#RequestMapping(value = "/add", method = RequestMethod.POST, params="hasCustomerName=false")
public String postDelivery2(
#ModelAttribute("deliveryDtoAttribute") #Valid DeliveryDto deliveryDto,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
model.addAttribute("deliveryDtoAttribute", deliveryDto);
model.addAttribute("customerNameList",
customerService.listAllCustomerNames());
model.addAttribute("customerCountryList", customerService
.listAllCustomerCountries(deliveryDto.getCustomerName()));
return "new-delivery";
} else {
Delivery delivery = new Delivery();
//Setters to set delivery values
return "redirect:/mis/home";
}
}
How come I get this error? Any help would be much appreciated! Thanks
EDIT: Changed hasId to hasCustomerName. I still get the HTTP Status 405 - Request method 'POST' not supported error though.
EDIT2: Commented out the line in the setFalse function that was causing the error
// D
I am not sure if this helps but I had the same problem.
You are using springSecurityFilterChain with CSRF protection. That means you have to send a token when you send a form via POST request. Try to add the next input to your form:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
Check if you are returning a #ResponseBody or a #ResponseStatus
I had a similar problem. My Controller looked like that:
#RequestMapping(value="/user", method = RequestMethod.POST)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
When calling with a POST request I always got the following error:
HTTP Status 405 - Request method 'POST' not supported
After a while I figured out that the method was actually called, but because there is no #ResponseBody and no #ResponseStatus Spring MVC raises the error.
To fix this simply add a #ResponseBody
#RequestMapping(value="/user", method = RequestMethod.POST)
public #ResponseBody String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
or a #ResponseStatus to your method.
#RequestMapping(value="/user", method = RequestMethod.POST)
#ResponseStatus(value=HttpStatus.OK)
public String updateUser(#RequestBody User user){
return userService.updateUser(user).getId();
}
You might need to change the line
#RequestMapping(value = "/add", method = RequestMethod.GET)
to
#RequestMapping(value = "/add", method = {RequestMethod.GET,RequestMethod.POST})
The problem is that your controller expect a parameter hasId=false or hasId=true, but you are not passing that. Your hidden field has the id hasId but is passed as hasCustomerName, so no mapping matches.
Either change the path of the hidden field to hasId or the mapping parameter to expect hasCustomerName=true or hasCustomerName=false.
I found the problem that was causing the HTTP error.
In the setFalse() function that is triggered by the Save button my code was trying to submit the form that contained the button.
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit();
document.submitForm.submit();
when I remove the document.submitForm.submit(); it works:
function setFalse(){
document.getElementById("hasId").value ="false";
document.deliveryForm.submit()
#Roger Lindsjö Thank you for spotting my error where I wasn't passing on the right parameter!
I was getting similar problem for other reason (url pattern test-response not added in csrf token)
I resolved it by allowing my URL pattern in following property in config/local.properties:
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$
modified to
csrf.allowed.url.patterns = /[^/]+(/[^?])+(sop-response)$,/[^/]+(/[^?])+(merchant_callback)$,/[^/]+(/[^?])+(hop-response)$,/[^/]+(/[^?])+(test-response)$
In my case the url was ending with /
paymentUrl(old)= /get-details/
i just removed the trailing /
paymentUrl(new)= /get-details
and it worked