400 the request sent by the client was syntactically incorrect - spring

I have gone through so many examples of this nature and proposed solutions from this site, but none of the solutions provided thereon apply to my problem. I believe that this error message, 400, shows up when the information sent to the controller is mulformed. I spent the last two days cross referrencing to another project I worked on in the past, which works, but I cannot pick up the problem.
#RequestMapping(value = {"/", "/home"}, method = RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("user", getPrincipal());
Catalog catalog = catalogService.getCatalogByCategory(Catalog.CatalogCategory.ALL);
model.addAttribute("catalog", catalog);
return "welcome";
}
This sends the data to a JSTL Spring form on my JSP as follows:
<form:form method="POST" modelAttribute="catalog">
<form:hidden path="id"/>
<form:hidden path="name"/>
<form:hidden path="category"/>
<form:hidden path="orderItems"/>
<div id="products" class="row list-group">
<c:forEach var="orderItem" items="${catalog.orderItems}">
<div class="item col-xs-4 col-lg-4">
<div class="thumbnail">
<img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt=""/>
<div class="caption">
<h4 class="group inner list-group-item-heading">
${orderItem.name}</h4>
<p class="group inner list-group-item-text">
${orderItem.description}
</p>
<div class="row">
<div class="col-xs-12 col-md-6">
<p class="lead">
R ${orderItem.price}</p>
</div>
<div class="col-xs-12 col-md-6">
<label for="${orderItem.id}" class="btn btn-primary">Add to Cart <input
type="checkbox" id="${orderItem.id}" name="orderItem.addedToCart"
class="badgebox"><span class="badge">&check;</span></label>
</div>
</div>
</div>
</div>
</div>
</c:forEach>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-12 pull-right">
</div>
<div class="col-sm-2 pull-right">
<input type="submit"
class="btn btn-default btn-block btn-primary"
value="Next" name="action" formmethod="POST"
formaction="confirmList"/>
</div>
</div>
</div>
</form:form>`
At this point I submit the form to the following listener in my controller:
#RequestMapping(value = "/confirmList", method = RequestMethod.POST)
public String confirmList(#ModelAttribute Catalog catalog, #ModelAttribute String numberOfItemsAdded) {
List<OrderItem> selectedItems = new ArrayList<OrderItem>();
for (OrderItem orderItem : catalog.getOrderItems()) {
if (orderItem.isAddedToCart()) {
selectedItems.add(orderItem);
}
}
//model.addAttribute("numberOfItemsAdded", selectedItems.size());
return "welcome";
}
That's it, execution flow does NOT even reach back my controller. Exhausting bug because I really do not understand what I am doing wrong here. Thank you in advance
EDIT:
Catalog.java
#Entity
#Table(name="Catalogs")
public class Catalog{
private long id; //generated value using hibernate ...
private String name; //column annotated by #Column
private String category;// column also annotated by #Column
private List<OrderItem> orderItems;// one to many mapping
//getters and setters here
}

I tested your code and I got HTTP 400 too. The thing is that what browser sends does not match whith what the controller method confirmList expects:
This is the form data I saw in Chrome's network tab:
id:1
name:the catalog
category:category
orderItems:[com.eej.ssba2.model.test.catalog.OrderItem#82ea8a, com.eej.ssba2.model.test.catalog.OrderItem#f441ae, com.eej.ssba2.model.test.catalog.OrderItem#40a13, com.eej.ssba2.model.test.catalog.OrderItem#1316c95, com.eej.ssba2.model.test.catalog.OrderItem#1cfc05a, com.eej.ssba2.model.test.catalog.OrderItem#5d725c, com.eej.ssba2.model.test.catalog.OrderItem#ff32b9, com.eej.ssba2.model.test.catalog.OrderItem#5b49a4, com.eej.ssba2.model.test.catalog.OrderItem#13faf31, com.eej.ssba2.model.test.catalog.OrderItem#6d64d]
orderItem.addedToCart:on
orderItem.addedToCart:on
orderItem.addedToCart:on
orderItem.addedToCart:on
action:Next
But controller cannot understand this, as OrderItems shows a toString() of each OrderItem instance and the addedToCart is not binded to any orderItem of the orderItems list.
You must modify your jsp this way:
<form:form method="POST" modelAttribute="catalog">
<form:hidden path="id"/>
<form:hidden path="name"/>
<form:hidden path="category"/>
<!-- form:hidden path="orderItems"/-->
<div id="products" class="row list-group">
<c:forEach var="orderItem" items="${catalog.orderItems}" varStatus="status">
<div class="item col-xs-4 col-lg-4">
<div class="thumbnail">
<img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt=""/>
<div class="caption">
<h4 class="group inner list-group-item-heading">
${orderItem.name}</h4>
<form:hidden path="orderItems[${status.index}].name" />
<p class="group inner list-group-item-text">
${orderItem.description}
<form:hidden path="orderItems[${status.index}].description" />
</p>
<div class="row">
<div class="col-xs-12 col-md-6">
<p class="lead">
R ${orderItem.price}</p>
<form:hidden path="orderItems[${status.index}].price" />
</div>
<div class="col-xs-12 col-md-6">
<label for="${orderItem.id}" class="btn btn-primary">Add to Cart <input
type="checkbox" id="${orderItem.id}" name="catalog.orderItems[${status.index}].addedToCart"
class="badgebox"><span class="badge">&check;</span></label>
</div>
</div>
</div>
</div>
</div>
</c:forEach>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-12 pull-right">
</div>
<div class="col-sm-2 pull-right">
<input type="submit"
class="btn btn-default btn-block btn-primary"
value="Next" name="action" formmethod="POST"
formaction="confirmList"/>
</div>
</div>
</div>
</form:form>
If you do so, you could see the message changes in Chrome's network tab (or the browser you are using). This is the form data right now:
id:1
name:the catalog
category:category
orderItems[0].name:OrderItemName#0
orderItems[0].description:OrderItemDesc#0
orderItems[0].price:0.0
orderItems[1].name:OrderItemName#1
orderItems[1].description:OrderItemDesc#1
orderItems[1].price:0.43645913001303904
orderItems[2].name:OrderItemName#2
orderItems[2].description:OrderItemDesc#2
orderItems[2].price:1.7151992716801088
orderItems[3].name:OrderItemName#3
orderItems[3].description:OrderItemDesc#3
orderItems[3].price:1.303683806806788
orderItems[4].name:OrderItemName#4
orderItems[4].description:OrderItemDesc#4
orderItems[4].price:2.507039003743686
orderItems[5].name:OrderItemName#5
orderItems[5].description:OrderItemDesc#5
orderItems[5].price:3.173744751378154
orderItems[6].name:OrderItemName#6
orderItems[6].description:OrderItemDesc#6
orderItems[6].price:3.183771167856446
catalog.orderItems[6].addedToCart:on
orderItems[7].name:OrderItemName#7
orderItems[7].description:OrderItemDesc#7
orderItems[7].price:6.73370053587355
catalog.orderItems[7].addedToCart:on
orderItems[8].name:OrderItemName#8
orderItems[8].description:OrderItemDesc#8
orderItems[8].price:2.0266022634803216
orderItems[9].name:OrderItemName#9
orderItems[9].description:OrderItemDesc#9
orderItems[9].price:5.251986962977732
catalog.orderItems[9].addedToCart:on
action:Next
And you would see now it returns a HTTP 200 as the request in fact reaches your controller. Delete the #ModelAttribute in your controller method too, as you have been suggested to:
#RequestMapping(value = "/confirmList", method = RequestMethod.POST)
public String confirmList(Catalog catalog, String numberOfItemsAdded) {
List<OrderItem> selectedItems = new ArrayList<OrderItem>();
for (OrderItem orderItem : catalog.getOrderItems()) {
if (orderItem.isAddedToCart()) {
selectedItems.add(orderItem);
}
}
//model.addAttribute("numberOfItemsAdded", selectedItems.size());
return "catalog";
}

try this extra handle in your Controller and Check your Console,
#ExceptionHandler(MissingServletRequestParameterException.class)
public void handleMissingRequestParams(MissingServletRequestParameterException ex) {
System.out.println();
System.out.println("------------------------MissingServletRequestParameterException------------------------");
System.out.println(" Parameter name:- "+ex.getParameterName());
System.out.println(" Parameter Type:- "+ex.getParameterType());
System.out.println(" Cause:- "+ex.getCause());
System.out.println(" LocalizedMessage:- "+ex.getLocalizedMessage());
System.out.println(" Message:- "+ex.getMessage());
System.out.println(" RootCause:- "+ex.getRootCause());
System.out.println("-------------------------------********************-----------------------------");
}
Edit
#kholofelo Maloma I can't see the variable numberOfItemsAdded in your jsp
AND i never used #ModelAttribute String numberOfItemsAdded to String parameter plz check this in documentation, I think it is used along with Bean, plz confirm
rather use #RequestParam
Read Documentation Here
It clears me
Sorry for above handle,
Note however that reference data and all other model content is not available to web views when request processing results in an Exception since the exception could be raised at any time making the content of the model unreliable. For this reason #ExceptionHandler methods do not provide access to a Model argument.

Related

The server cannot process the request because of Spring MVC <form:select>

I am creating a formular in order to populate some entities. I run into problems when I am trying to POST a form which contains a Spring MVC <form:select> field.
In Eclipse I do not receive any error or warning message, while in the browser I get a http status 400 - Bad Request.
Type Status Report
Description The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
Extra explanations:
I have a StudentDetails entity which contains 2 fields that I am interested in at the moment, Classroom classroom and ParentsDetails parentsDetails.
The Classroom objects are already created and all of them will be stored in a LinkedHashMap<Classroom, String> as a model attribute (i am doing this in the saveAccountDetails method from the Controller).
The ParentsDetails object will be created after the StudentDetails entity will be saved with the selected classroom.
When I submit the form as I mentioned above I run into an error but without any(or relevant) error message.
I spent some time debugging and trying different approaches of handling that map of Classrooms, but none of them worked.
What is actually happening, the controller method saveStudentDetails is not called anymore.
The issue must come from that form:select because if I get rid of this input, the controller method will be called and will let me advance in creating the ParentsDetails entity.
I have no clue what is wrong.
I used previously this form:select but the LinkedHashMap contained just Strings, without any objects and it worked. I think thats my issue.
StudentDetails.java
#Entity
#Table(name="student_details")
public class StudentDetails {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="current_year_of_study")
private Integer currentYearOfStudy;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="parents_details_id")
private ParentsDetails parentsDetails;
#ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="class_id")
private Classroom classroom;
#OneToOne(mappedBy="studentDetails", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
private User user;
#ManyToMany
#JoinTable(name="course_studentdetails",
joinColumns=#JoinColumn(name="student_details_id"),
inverseJoinColumns=#JoinColumn(name="course_id")
)
private List<Course> courses;
... (Constructors, getters setters)
Controller.java
#PostMapping("/save-account-details")
public String saveAccountDetails(#ModelAttribute("theAccountDetails") AccountDetails theAccountDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.setAccountDetails(theAccountDetails);
accountDetailsService.saveAccountDetails(theAccountDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theStudentDetails", new StudentDetails());
theModel.addAttribute("classroomsList", classroomService.getSchoolClassrooms(theUser.getAccountDetails().getCity()));
theModel.addAttribute("entity", "StudentDetails");
return "create-user";
}
#PostMapping("/save-student-details")
public String saveStudentDetails(#ModelAttribute("theStudentDetails") StudentDetails theStudentDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.setStudentDetails(theStudentDetails);
studentDetailsService.saveStudentDetails(theStudentDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theParentsDetails", new ParentsDetails());
theModel.addAttribute("entity", "ParentsDetails");
return "create-user";
}
#PostMapping("/save-parents-details")
public String saveParentsDetails(#ModelAttribute("theParentsDetails") ParentsDetails theParentsDetails, #RequestParam("userUsername") String username, Model theModel) {
User theUser = userService.getUser(username);
theUser.getStudentDetails().setParentsDetails(theParentsDetails);
parentsDetailsService.saveParentsDetails(theParentsDetails);
userService.saveUser(theUser);
theModel.addAttribute("theUser", theUser);
theModel.addAttribute("theParentsDetails", new ParentsDetails());
theModel.addAttribute("entity", "ParentsDetails");
return "create-user";
}
create-user.jsp
<c:if test="${entity == 'StudentDetails'}">
<c:url var="saveStudentDetails" value="save-student-details">
<c:param name="userUsername" value="${theUser.username}" />
</c:url>
<form:form action="${saveStudentDetails}" modelAttribute="theStudentDetails" method="POST">
<form:hidden path="id" />
<div class="form-area">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Current Year of Study</span>
</div>
<form:input type="text" class="form-control" path="currentYearOfStudy" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<label class="input-group-text" for="inputGroupSelect01">Classroom</label>
</div>
<form:select path="classroom" class="custom-select" id="inputGroupSelect01">
<form:option value="" label="Select classroom..." />
<form:options items="${classroomsList}" />
</form:select>
</div>
<button type="submit" class="btn btn-outline-secondary btn-block">Submit</button>
</div>
</form:form>
</c:if>
<c:if test="${entity == 'ParentsDetails'}">
<c:url var="saveParentsDetails" value="save-parents-details">
<c:param name="userUsername" value="${theUser.username}" />
</c:url>
<form:form action="${saveParentsDetails}" modelAttribute="theParentsDetails" method="POST">
<form:hidden path="id" />
<div class="form-area">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father First Name</span>
</div>
<form:input type="text" class="form-control" path="fatherFirstName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father Last Name</span>
</div>
<form:input type="text" class="form-control" path="fatherLastName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Father Telephone</span>
</div>
<form:input type="text" class="form-control" path="fatherTelephone" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother First Name</span>
</div>
<form:input type="text" class="form-control" path="motherFirstName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother Last Name</span>
</div>
<form:input type="text" class="form-control" path="motherLastName" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Mother Telephone</span>
</div>
<form:input type="text" class="form-control" path="motherTelephone" aria-label="Default" aria-describedby="inputGroup-sizing-default" />
</div>
<button type="submit" class="btn btn-outline-secondary btn-block">Submit</button>
</div>
</form:form>
</c:if>
If any more code snippets are needed, I will add them as soon as possible. Thank you in advance!
This is the network tab. The object looks like it was submitted with the form...
I will add shortly a video with the application.
Edit: demo link: https://youtu.be/neJOLHL9REo
Try adding multipart form-data to enctype in the form tag, it may works.
FROM:
<form:form action="${saveStudentDetails}"
modelAttribute="theStudentDetails" method="POST">
TO:
<form:form action="${saveStudentDetails}" enctype="multipart/form-data"
modelAttribute="theStudentDetails" method="POST">

pass object from HTML template back to the controller

I have the following HTML block. I want to pass the object "jobDTO" back to the contoller "/deleteJob" method. Whatever I do I am getting null object.
<th:block th:if="${jobDTO != null}" th:each="jobDTO: ${allJobDTOs.get(jobGroup)}">
<div id="accordion2" style="margin-bottom: 3px;">
<div class="card" id="headingOne">
<div class="card-header" style="padding: 0">
<h5 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" th:attr="data-target='#accordion2_'+${jobDTO.identity.name}"
aria-expanded="true" aria-controls="collapseChild" >
<p class="font-weight-bold custom-p identity-black" > Job Identity </p>
<p class="custom-p" style="padding-left: 52px;" th:text="${jobDTO.identity.group} +' , ' + ${jobDTO.identity.name}"></p>
</button>
</h5>
</div>
<div th:id="'accordion2_'+${jobDTO.identity.name}" class="collapse" aria-labelledby="headingOne" data-parent="#accordion2">
<div class="card-body">
<dl class="row">
<dt class="col-lg-3">Trigger List</dt>
<dd class="col-sm-9">
<th:block th:each="trigger: ${jobDTO.triggers}">
<p><b>nextFireTime</b> <span th:text="${trigger.nextFireTime}"> </span></p>
<hr>
</th:block>
</dd>
</dl>
<!-- important part.. how to pass the jobDTO object back to the controller -->
<form id="form2" action="#" th:action="#{/deleteJob}" th:object="${jobDTO}" th:method="post">
<input type="text" th:value="*{identity.name}" th:field="*{identity.name}" hidden/>
<button type="submit" value="Submit" class="btn btn-danger btn-sm" >Delete Job</button>
</form>
</div>
</div>
</div>
</div>
</th:block>
my controller relevant parts are:
#GetMapping(value = "/deleteJob")
public String deleteJobPage(Model model) {
model.addAttribute("jobDTO", new ScheduleJobDTO());
//Returns the Home page with the prepared model attributes
return "Home";
}
// =================
#PostMapping("/deleteJob")
public String deleteJob(#ModelAttribute final ScheduleJobDTO jobDTOReturn, BindingResult bindingResult, Model model) {
// I want to receive the jobDTO object here
schedulerService.deleteJobFromGroup(jobDTOReturn.getIdentity().getGroup(),
jobDTOReturn.getIdentity().getName());
return "redirect:/";
}
what I am missing here?
I think there is an error in your input tag, try this :
<input type="text" th:value="${jobDTO.identity.name}" th:field="*{identity.name}" hidden/>

How to submit form jsp to controller?

In my jsp there are two fields: 1 types of documents and 2 are statuses.
The challenge is that I can send the selection to my controller. I can not figure out jsp how to send by action these values ​​at the click of a button
<div id="page-content-wrapper">
<div class="container-fluid">
<div class="card">
<div id="collapseSearchParams" class="collapse show" aria-labelledby="headingSearchParams" >
<div class="card-body">
<form:form action="${request.contextPath}/search" method="post">
<div class="row">
<div class="col-lg-3 col-md-4 col-sm-6">
<fieldset class="form-group">
<label class="form-label semibold" for="typeCode">${phDocType}</label>
<select class="form-control" id="typeCode">
<c:forEach var="type" items="${documentList}">
<option id = "${type.value}"> ${type.name} </ option>
</c:forEach>
</select>
</fieldset>
</div>
<div class="col-lg-3 col-md-4 col-sm-6">
<fieldset class="form-group">
<label class="form-label semibold" for="statusCode">${phOrderStatus}</label>
<select class="form-control" id="statusCode">
<option> A</ option>
<option> B</option>
<option> C</option>
<option> D</option>
</select>
</fieldset>
</div>
<div class="col-sm-12">
<button id ="btnSearchUsers" class="btn btn-sm btn-secondary" style="float: right; "><span class="fa fa-search"></span> <spring:message code="label.button.search" /></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
How to get this data in the controller to process? What changes should I make in jsp and in the method?
#RequestMapping(value = "/perso")
public class PersoController {
#Autowired
private PersoService persoService;
private List<DocumentType> docTypeList = null;
#RequestMapping(value = "/list")
public String persoList(Principal principal, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
docTypeList = persoService.persoList(authentication, principal);
model.addAttribute("documentList", docTypeList);
return "admin/perso/list";
}
#RequestMapping(value = "/search", method = RequestMethod.POST)
public void searchOrders( ){
}
}
thanks in advance
It's better to use #RequestParam in your controller.
For Example:
#RequestMapping(value = "/bar")
public String testAction(#RequestParam String fieldName) {
//Do whatever you want to fieldName
return "view";
}
For your case
#RequestMapping(value = "/search", method = RequestMethod.POST)
public void searchOrders(#RequestParam String statusCode ,#RequestParam String typeCode)
{
//Do whatever you want
}

How tell the view an error has occured from controller? (Spring MVC)

How can I say trigger error/validation messages in the view from the controller in a better way? Currently, I do this by sending boolean attributes. For example, in creating a product, I have two possible errors. Invalid format of UPC of a product, or duplicate upc. I also have a validati
#RequestMapping("/createProduct")
public String createProduct(Model model, #RequestParam(value = "name") String name,
#RequestParam(value = "upc") String upc, #RequestParam(value = "category") String categoryName,
#RequestParam(value = "description") String description, #RequestParam(value = "price") BigDecimal price,
#RequestParam(value = "stock") int stock){
model.addAttribute("activeTab", 3);
if(Validator.invalidUpcFormat(upc)){
model.addAttribute("invalidFormat", true); //trigger for invalid format
return "management";
}
Category category = productService.getCategory(categoryName);
Product product = new Product(upc, category, name, description, price);
InventoryProduct inventoryProduct = new InventoryProduct(product, stock);
try {
managerService.add(inventoryProduct);
model.addAttribute("productCreated", true);
} catch (DuplicateProductException e) {
model.addAttribute("upc", upc);
model.addAttribute("duplicateProduct", true); // trigger for duplicate product
}
return "management";
}
And here is my view:
<div id="menu3"
class="tab-pane fade <c:if test="${activeTab == 3}">in active</c:if>">
<div class="container-fluid" style="padding: 2%;">
<div class="row">
<div class="col-md-12"
style="padding-left: 15%; padding-right: 15%;">
<c:if test="${productCreated}">
<div class="alert alert-success fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Success!</strong>
Product has been created!
</div>
</c:if>
<c:if test="${duplicateProduct}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oh no!</strong>
Product with the UPC ${upc} already exists!
</div>
</c:if>
<c:if test="${invalidFormat}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oops!</strong>
Invalid UPC format!
</div>
</c:if>
<form
action="${pageContext.request.contextPath}/manager/createProduct"
method="post">
<div class="form-group">
<label for="Name">Name </label> <input type="text" name="name"
class="form-control" required />
</div>
<div class="form-group">
<label for="UPC">UPC </label> <input type="number" name="upc"
class="form-control" required />
</div>
<div class="form-group">
<div class="form-group">
<label for="category">Category</label> <select
class="form-control" name="category" required>
<option selected disabled value="">SELECT CATEGORY</option>
<c:forEach items="${categories}" var="item">
<option>${item.getName()}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" rows="5" name="description"></textarea>
</div>
<div class="form-group">
<label for="price">Price </label> <input type="number"
name="price" class="form-control" required />
</div>
<div class="form-group">
<label for="stock">Stock </label> <input type="number"
name="stock" class="form-control" required />
</div>
<button type="submit" class="btn btn-primary">Add
product</button>
</form>
</div>
</div>
</div>
</div>
Is there a better of doing this other than sending boolean triggers?
You could use Spring BindingResult. This is typical filled with the result of Binding and Validation results. But you can also add errors by hand.
But first you need to refactor your code, so that you use an single command/form-backing object instead of all the #Param values
public class CreateProductCommand {
private String name;
private String upc;
private String categoryName;
.... //other fields
public CreateProductCommand (){} //parameter less conturctor
Getter+Setter
}
Controller
#RequestMapping("/createProduct")
public ModelAndView createProduct(CreateProductCommand createProductCommand, BindingResult bindingResult) //Binding result must be the parameter direct next to the object that should been validated!!!
{
if (someustomValidationForUcpFail()) {
bindingResult.rejectValue("upc", //the field name of the invalid field
"error.Message.Key",
"Default Error Message");
}
if (bindingResult.hasErrors()) {
ModelMap model = new ModelMap();
model.add("createProductCommand", createProductCommand);
return new ModelAndView("createForm", model)
} else {
Product product = ...
return new ModelAndView("showProduct", "product", product)
}
}
jsp:
You need to use springs form and input tag:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:springForm="http://www.springframework.org/tags/form"
version="2.0">
....
<springForm:form action="<c:url value="/manager/createProduct">" method="POST" modelAttribute="createProductCommand">
<springForm:input path="name"/> <form:errors path="name" />
<springForm:input path="ucp"/> <form:errors path="ucp" />
....
</springForm:form>
....

Submitting a POST object

I spent the whole weekend searching the net on my problem. It seems like I am missing something really silly, but I failed to pick it up.
Here's the problem. I sent an object to a JSP and on the JSP I could see its content. I them I submitted the form. Back in the controller, it shows the object is overwritten/recreated. I can't seem to understand. I checked the logs on my Tomcat but I do not see any error...
On my Controller:
#RequestMapping(value = {"/", "/home"}, method = RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("user", getPrincipal());
Catalog catalog = catalogService.getCatalogByCategory(Catalog.CatalogCategory.ALL);
model.addAttribute("catalog", catalog);
model.addAttribute("numberOfItemsAdded", "500");
return "welcome";
}`
and in my JSP I have the following:
<form:form method="POST" modelAttribute="catalog">
<form:hidden path="id"/>
<div id="products" class="row list-group">
<c:forEach var="orderItem" items="${catalog.orderItems}">
<div class="item col-xs-4 col-lg-4">
<div class="thumbnail">
<img class="group list-group-image" src="http://placehold.it/400x250/000/fff" alt=""/>
<div class="caption">
<h4 class="group inner list-group-item-heading">
${orderItem.name}</h4>
<p class="group inner list-group-item-text">
${orderItem.description}
</p>
<div class="row">
<div class="col-xs-12 col-md-6">
<p class="lead">
R ${orderItem.price}</p>
</div>
<div class="col-xs-12 col-md-6">
<label for="${orderItem.id}" class="btn btn-primary">Add to Cart <input
type="checkbox" id="${orderItem.id}" name="orderItem.addedToCart"
class="badgebox"><span class="badge">&check;</span></label>
</div>
</div>
</div>
</div>
</div>
</c:forEach>
</div>
<div class="row">
<div class="form-group">
<div class="col-sm-12 pull-right">
</div>
<div class="col-sm-2 pull-right">
<input type="submit"
class="btn btn-default btn-block btn-primary"
value="Next" name="action" formmethod="POST"
formaction="confirmList"/>
</div>
</div>
</div>
</form:form>`
After pressing "Next", which submits the form data to a controller:
#RequestMapping(value = "/confirmList", method = RequestMethod.POST)
public String confirmList(#ModelAttribute Catalog catalog, #ModelAttribute String numberOfItemsAdded) {
System.out.println("\n\n------>catalog = " + catalog);
System.out.println("\n\n------>numberOfItemsAdded = " + numberOfItemsAdded);
List<OrderItem> selectedItems = new ArrayList<OrderItem>();
for (OrderItem orderItem : catalog.getOrderItems()) {
if (orderItem.isAddedToCart()) {
selectedItems.add(orderItem);
}
}
//model.addAttribute("numberOfItemsAdded", selectedItems.size());
return "welcome";
}
`
The System.out.println(...) output the following:
------>catalog = Catalog{id=1, name='null', category='null', orderItems=null}
------>numberOfItemsAdded =
Those are empty outputs.... :'(
I have no idea what I am doing wrong here.....
This was answered in a comment line by George. All I had to do is hide/bind those null fields.
It is very concerning that we use the term 'hidden' to also mean 'ignore' or 'leave as is'. But anyway, the solution above did exactly what I was looking for.
Thanks to the Community

Resources