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.
Related
Please help me to solve this problem: I have no idea how to transfer data from thymeleaf view to controller, that expected data as a hash map? So let me explain in more detail.
I have next pojo, that is used as a wrapper for my hashMap data. It looks like this:
public class DTDays {
private Map<Driver, String> driversDays = new HashMap<>();
public Map<Driver, String> getDriversDays() {
return driversDays;
}
public void setDriversDays(Map<Driver, String> driversDays) {
this.driversDays = driversDays;
}
}
Controller has method with parameter model attribute and another pojo:
#RequestMapping(value = "/go", method = RequestMethod.POST)
public String advanced(#ModelAttribute DTDays dtDays,
#RequestParam(name = "tourId") Long tourId, Model model){
// make some business logic with provided data
return "redirect:/tours/advanced";
}
Here I debugged and dtDays is not null, but map property is empty, also tourId is worked as expected, I can get right value.
Now to the problem, view:
<body>
<div style="margin-left: 20px;">
<h1 th:text="${tour.tittle}"></h1>
<p>Add tour interval for drivers</p>
<form id="driverIntervals" th:action="#{/advanced/go}" th:object="${driversDays}" method="post">
<table>
<tr>
<td>Drivers:</td>
<td>Date:</td>
</tr>
<tr th:each="d: ${attachedDrivers}">
<td th:text="${d.id+' '+d.fullName}" >
<input type="hidden" th:value="${d.id}" >
</td>
<td>
<input type="text" placeholder="Pick days" th:name="days"/>
</td>
</tr>
</table>
<input type="hidden" th:name="tourId" th:value="${tour.id}"/>
<button type="submit">Confirm</button>
</form>
</div>
</body>
View looks like this:
What should i write in view to submit data? In my case Driver is a key of the map and user entered data in related input field will be a value of a map.
I already know how to submit List, by using select-option in a view:
<select th:name="drivers2attach" multiple="multiple" id="attachDrivers">
<!--/*#thymesVar id="drivers" type="java.util.List<stanislav.tun.novinomad.picasso.persistance.pojos.Driver>"*/-->
<option th:each="d : ${drivers}" th:value="${d.id}"
th:text="${d.getId()+' '+d.fullName}">
</option>
</select>
and #RequestParam List list in controller:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public ModelAndView addTourAction(#ModelAttribute("tour") Tour tour,
#RequestParam(required = false, name = "drivers2attach") List<Long> drivers2attach)
But how to deal with map?
In case with list data is auto populated. In map only keys is prepopulated, this is count of drivers, and now i expect user input for each driver as a key value.
In researching of answers i already read these sources:
How to bind an object list with thymeleaf?
Send list object from thymeleaf to controller
How do I load HashMap and ModelandView object values using Thymeleaf in a Spring Boot application?
Thymeleaf Map Form Binding
http://forum.thymeleaf.org/How-to-use-Map-lt-String-String-gt-with-Spring-and-Thymeleaf-forms-td4028666.html
http://forum.thymeleaf.org/How-to-use-method-POST-with-a-complex-Hashmap-lt-Object-list-lt-Object-gt-gt-td4031257.html
Thymeleaf Map Form Binding
Use HashMap as Form Backing Bean Spring MVC + ThymeLeaf
etc.
But didn't helps. Somewhere in this links I found out that I should use some wrapper to do it, but again no idea why it not works, or what should I do additionally for make it working. Maybe I generally make wrong logic and to submit data as hashmap I shall convert data to list first and then somehow get map from it in controller?
Sorry for creating duplicate question, finally i found solution by following this answer on stackoverflow
i wrote little project for test it and it works. So i was on right way in controller with a wrapper. I missed only view map representation and syntax;
Finally view will looks like this:
view
here is view source code:
<form th:action="#{/save}" th:object="${form}" method="post">
<h1>Welcome</h1>
<div th:each="property : ${form.properties.entrySet()}">
<div class="form-group">
<label th:for="*{properties['__${property.key}__']}" th:text="${property.key}">Property</label>
<input type="text" class="form-control" th:field="*{properties['__${property.key}__']}" />
</div>
</div>
<button type="submit">send to controller</button>
and controller:
#PostMapping("/save")
public ModelAndView save(#ModelAttribute(name = "form") MapWrapper form){
var mav = new ModelAndView();
mav.setViewName("index");
mav.addObject("mapWrapper", form);
var map = form.getProperties();
System.out.println("Size of map = "+map.size());
for (Long id : map.keySet()) {
System.out.println("Key: "+id+"; Value: "+map.get(id));
}
return mav;
}
and output is :
output
P.S. reason of asking here is that i stuck on this problem for about 2 weeks, I despaired, but found solution after create a question.
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.
So, i am developing a web service where when i call api/update action on form submit, two of my inputs from my jsp, "data" and "id" are supposed to pass, on which i want to perform operations in the POST method block.
here is the form calling the method:
<form id="myForm" action="api/update" method="post">
id: <input type="text" id="id" name="id">
<br/>
<textarea id="data" name="data" rows="30" cols="100" placeholder="Empty File"></textarea>
<br/>
<button id="submit" type="submit">Submit</button>
</form>
Here is the REST code:
#Path("update")
public class UpdateResource {
#Context
private UriInfo context;
public UpdateResource() {
}
#Context
private HttpServletRequest request;
#POST
public void putHtml() {
String data = request.getParameter("data");
String id = request.getParameter("id");
System.out.println(id); //<---- line1
System.out.println(data); //<---- line2
//further operations
}
}
The problem is that line1 and line2 gives null as output, means somehow data is not being passed. Am i doing something wrong?
Here is the Header file captured from network tab of chrome browser:
Instead of trying to get the parameters from the HttpServletRequest, use #FormParam
#POST
public void putHtml(#FormParam("id") String id,
#FormParam("data") String data) {
}
The reason is that the entity body is already being read, resulting in the parameters being empty in the HttpServletRequest.
In my Controller I have
#RequestMapping(value="/getCounties", method = {RequestMethod.GET},produces=MediaType.Application_JSON_VALUE)
public #Responcebody List<Counties>(#RequestParam String province){
List<Counties> county = this.xService.getCounties(county);
return county;
}
This method send the province chosen in the form down to the repository and join on the counties within that province.
In my dropdown on the form how do I return these values into the dropdown.
I currently have
<tr>
<td>
<form:select path="cdGcd" class="textbox" onclick="getCounty()">
<form:option value="-" label="Please Select"/>
<form:options path="county" items='${county}' itemValue = "countycode" itemLabel="countydescription"/>
</form:select>
</td>
</tr>
You can not return List directly form controller.
For passing data from controller to JSP you need to add data in Model and return respective JSP page.
So you need to change your method to,
#RequestMapping(value="/getCounties", method = {RequestMethod.GET})
public String getCountries(#RequestParam String province, Model model){
List<Counties> county = this.xService.getCounties(county);
model.addAttribute("county",county);
return "jsp page";
}
If you want to achieve this using AJAX then,you need to return JsonObject from controller.
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