I'm trying to iterate an input field thru GET in Java Controller and thymeleaf but it's giving me the error of "result returns more than one elements"
My GET can receive the Array of String ID's but I can't put them in my Form, I will use my Form to POST it and delete the Array of String ID's. It's working if the user input is only one but when the user input becomes 2 or more it shows the error "result returns more than one elements"
This is my code on Java Controller
#RequestMapping(value="/delete-asset", method = RequestMethod.GET)
public String deleteAsset(#RequestParam String[] assetID, Model model) {
//to populate the form
model.addAttribute("DeleteCategoryObject", assetService.getAssetInfo(assetID));
return "delete-asset";
}
This is my code on Html Form
<form method="POST" name="deleteFormAdd" id="deleteFormAdd" th:object="${DeleteCategoryObject}"enctype="multipart/form-data">
<input type="hidden" name="_csrf" th:value="${_csrf.token}" />
//The code can't iterate the String of array given by the GET
<input type="hidden" th:field="*{assetID}" />
//This button is for submitting the String of array ID to be deleted
<input type="button" class="btn btn-primary btn-block" value="Yes" th:onclick="'javascript:submitForm(\'deleteFormAdd\',\''+#{/delete-asset}+'\')'" />
<button type="reset" onclick="window.location.href = 'manage-assets.html';" class="btn btn-default btn-block"> Cancel</button>
</form>
The code can accept this link
But it shows the error "result returns more than one elements" When the given ID is 2 or more
Full Error
org.springframework.dao.IncorrectResultSizeDataAccessException: result returns more than one elements; nested exception is javax.persistence.NonUniqueResultException: result returns more than one elements
Java DAO/Query
#Query("From AssetCategory A WHERE A.assetID IN (:assetID)")
public AssetCategory getAssetInfo(#Param("assetID") String[] assetID);
AssetCategory
#Entity
#Table(name = "ASSET_TYPE")
public class AssetCategory implements Serializable{
private static final long serialVersionUID = 595169758417136780L;
#Id
#Column(name = "ASSET_ID")
#GenericGenerator(name="uuid", strategy="uuid")
#GeneratedValue(generator="uuid")
private String assetID;
#Column(name = "ASSET_TYPE")
private String assetType;
#Column(name = "CATEGORY")
private String category;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "CREATED_BY")
private String createdBy;
public AssetCategory() {
super();
public AssetCategory(String assetID, String assetType, String category, String description, String createdBy) {
super();
this.assetID = assetID;
this.assetType = assetType;
this.category = category;
this.description = description;
this.createdBy = createdBy;
}
//getters setters below
public String getAssetID() {
return assetID;
}
public void setAssetID(String assetID) {
this.assetID = assetID;
}
public String getAssetType() {
return assetType;
}
public void setAssetType(String assetType) {
this.assetType = assetType;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
Controller for Java Delete Post
#RequestMapping(value = "/delete-asset", method = RequestMethod.POST)
public #ResponseBody String deleteAsset(#ModelAttribute AssetCategory assetCategory) {
JsonObject result = new JsonObject();
assetService.deleteAssets(assetCategory.getAssetID());
result.addProperty("result", "Success");
result.addProperty("status", 1);
result.addProperty("message", "Asset Deleted!");
return result.toString();
}
Query of Delete POST
#Query("Delete From AssetCategory A WHERE A.assetID IN (:assetID)")
List<AssetCategory> deleteAssets(#Param("assetID") String[] assetID);
Delete POST Service
#Override
public List <AssetCategory> deleteAssets(String[] assetID) {
return dao.deleteAssets(assetID);
}
Submit Form function
function submitForm(formID, url){
var formData = new FormData($("#" + formID)[0]);
$.ajax({
dataType: 'json',
url: url,
data : formData,
type : "POST",
enctype : "multipart/form-data" ,
processData : false,
contentType : false,
success : function(data) {
if (data.status == 1) {
openAlertDialog("Success", "The Asset type has been deleted!", "Continue", "manage-assets");
} else {
openAlertDialog("Error", data.message, "Continue", "manage-assets");
}
},
error : function(data) {
openAlertDialog("Error", data.message, "Continue", "manage-assets");
},
});
}
Problem is with your query. When you provide array of assetID Query trying to return List of AssetCategory objects. But currently method allows to return only one AssetCategory object. That's why the error comes when you pass array of assetID. So you have to change the return type of getAssetInfo method as below:
#Query("From AssetCategory A WHERE A.assetID IN (:assetID)")
public List<AssetCategory> getAssetInfo(#Param("assetID") String[] assetID);
Now getAssetInfo method returns List of AssetCategory objects. Which means you are now passing List<AssetCategory> to DeleteCategoryObject model attribute. So you have to do necessary changes in your front end.
You can now iterate it in front end as below:
<form method="POST" name="deleteFormAdd" id="deleteFormAdd" enctype="multipart/form-data">
<input type="hidden" name="_csrf" th:value="${_csrf.token}" />
//The code can't iterate the String of array given by the GET
<input type="hidden" class="assetId" th:each="deleteCategory, itemStat : ${DeleteCategoryObject}" th:name="|DeleteCategoryObject[__${itemStat.index}__].assetID|" th:value="${deleteCategory.assetID}"/>
//This button is for submitting the String of array ID to be deleted
<input type="button" class="btn btn-primary btn-block" value="Yes" th:onclick="'javascript:submitForm(\'deleteFormAdd\',\''+#{/delete-asset}+'\')'" />
<button type="reset" onclick="window.location.href = 'manage-assets.html';" class="btn btn-default btn-block"> Cancel</button>
</form>
Change the Post controller as below:
#RequestMapping(value = "/delete-asset", method = RequestMethod.POST)
public #ResponseBody String deleteAsset(#ModelAttribute List<AssetCategory> assetCategories) {
JsonObject result = new JsonObject();
if (assetCategories != null && !assetCategories.isEmpty()) {
String[] arr = new String[assetCategories.size()];
for (int i =0; i < assetCategories.size(); i++) {
arr[i] = assetCategories.get(i).getAssetID();
}
assetService.deleteAssets(arr);
result.addProperty("result", "Success");
result.addProperty("status", 1);
result.addProperty("message", "Asset Deleted!");
}
return result.toString();
}
Submit form function:
function submitForm(formID, url) {
var assetIdList = [];
var assetIdObj;
$("#" + formID).find('.assetId').each(function () {
assetIdObj = {};
assetIdObj.assetID = $(this).val();
assetIdList.push(assetIdObj);
});
$.ajax({
dataType: 'json',
url: url,
data: {assetCategories: assetIdList},
type: "POST",
enctype: "multipart/form-data",
processData: false,
contentType: false,
success: function (data) {
if (data.status === 1) {
openAlertDialog("Success", "The Asset type has been deleted!", "Continue", "manage-assets");
} else {
openAlertDialog("Error", data.message, "Continue", "manage-assets");
}
},
error: function (data) {
openAlertDialog("Error", data.message, "Continue", "manage-assets");
},
});
}
Related
I am working on Java Spring project, and I have this code that allows me to edit specific Quote based on it's Id.
//Quote's details
#GetMapping("/profile/{id}")
public String blogDetailsId(#PathVariable(value="id") long id, Model model){
if(!quoteRepository.existsById(id)){
return "redirect:/profile";
}
Optional<Quote> post = quoteRepository.findById(id);
ArrayList<Quote> res = new ArrayList<>();
post.ifPresent(res::add);
model.addAttribute("post", res);
return "detail_quote";
}
#GetMapping("/profile/{id}/edit")
public String QuoteDetails(#PathVariable(value="id") long id, Model model) {
if(!quoteRepository.existsById(id)){
return "redirect:/profile";
}
Optional<Quote> post = quoteRepository.findById(id);
ArrayList<Quote> res = new ArrayList<>();
post.ifPresent(res::add);
model.addAttribute("post", res);
return "edit_quote";
}
//Save changes into database
#PostMapping("/profile/{id}/edit")
public String QuoteEdit(#PathVariable(value="id") long id, #RequestParam String quote, #RequestParam String author, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow(); //orElseThrow is used to throw exception when ID is not found.
post.setAuthor(author);
post.setQuote(quote);
post.setVotes(votes);
quoteRepository.save(post);
return "redirect:/profile";
}
Model code:
#Entity
public class Quote {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id; //Generate unique ID for every quote automatically.
private String quote, author;
private int votes;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getVotes() {
return votes;
}
public void setVotes(int votes) {
this.votes = votes;
}
public Quote() {}
public Quote(String quote, String author, int votes){
this.author = author;
this.quote = quote;
this.votes = votes;
}
}
This works as expected, and I am able to change the details of any Quote. But when I try to change specifically number of Votes by pressing upvote button - it results in following error:
There was an unexpected error (type=Bad Request, status=400).
Required parameter 'votes' is not present.
org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'votes' for method parameter type int is not present
The code for Voting:
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
The Button for Upvoting is stored inside 'detail_quote.html', which is functioning well, except for upvote button:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Quote's Details</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" crossorigin="anonymous">
<body>
<header th:insert="blocks/header :: header"></header>
<div class = "container mt-5">
<div th:each="el : ${post}" class="alert alert-info mt-2">
<h3 th:text="${el.quote}"></h3>
<p th:text="${el.author}"></p>
<p><b>Votes: </b><span th:text="${el.votes}"></span></p>
<a th:href="'/profile/' + ${el.id} + '/edit'" class="btn btn-warning">Edit</a><br>
<form th:action="'/profile/' + ${el.id} + '/upvote'" method="post"><br>
<button class="btn btn-warning" type="submit">Upvote</button><br>
</form>
<form th:action="'/profile/' + ${el.id} + '/downvote'" method="post">
<button class="btn btn-warning" type="submit">Downvote</button>
</form>
<form th:action="'/profile/' + ${el.id} + '/remove'" method="post"><br>
<button class="btn btn-warning" type="submit">Remove Quote</button><br>
</form>
</div>
</div>
<div th:insert="blocks/footer :: footer"></div>
</body>
</html>
So if anyone can explain to me why does it work when I change everything and does not work when I'm just trying to increase votes by 1 - I will be very thankful.
According to your code sample i could not see the args votes defined in your thymeleaf template as post request , but it is truely defined in backEnd Application
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam int votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
so that if you do a post request to url "/profile/{id}/upvote" and have no args with votes you will get error log
There was an unexpected error (type=Bad Request, status=400). Required parameter 'votes' is not present.
as suggestion ,i advice build the api as below sample
#PostMapping("/profile/{id}/upvote")
#ResponseBody
public String VoteUp(#PathVariable(value="id") long id, #RequestParam(required = false) Integer votes, Model model) {
Quote post = quoteRepository.findById(id).orElseThrow();
votes == null ? post.getVotes():votes;
post.setVotes(votes+1);
quoteRepository.save(post);
return "redirect:/profile";
}
Trying to access multiple objects in the POST method using SpringBoot MVC and thymeleaf.
here is the controller.
#Controller
public class PatientController {
ObjectMapper Obj = new ObjectMapper();
#GetMapping("/patient")
public static String patientForm(Model model) {
model.addAttribute("patient", new PatientDataModel());
model.addAttribute("patient1", new PatientDataModel1());
return "patient";
}
#RequestMapping(value="/patient", method=RequestMethod.POST, params="action=Send data to MongoDB cluster")
public static String patientSubmit(#ModelAttribute("patient") PatientDataModel patient, #ModelAttribute("patient1") PatientDataModel patient1, Model model, Object obj ) throws JsonProcessingException {
model.addAttribute("patient", patient);
model.addAttribute("patient1", patient1);
return "result";
}
and here are the views:
patient.html
<form action="#" th:action="#{/patient}" th:object="${patient}" method="post">
<div th:object="${patient1}" >
<p>Patient Id: <input type="text" th:value="${patient.id}" /></p>
<p>Patient Name: <input type="text" th:value="${patient.name}" /></p>
<p>Message: <input type="text" th:value="${patient.content}" /></p>
<p>address: <input type="text" th:value="${patient1.address}" /></p>
</div>
<p><input type="submit" name="action" value="Send data to MongoDB cluster" />
<input type="reset" value="Reset" /></p>
</form>
</div>
and result.html
<div class="starter-template">
<h1>Result</h1>
<p th:text="'id: ' + ${patient.id}" />
<p th:text="'Name: ' + ${patient.name}" />
<p th:text="'content: ' + ${patient.content}" />
<p th:text="'address: ' + ${patient1.address}" />
Submit another message
</div>
and the bean classes are : PatientDataModel.java
public class PatientDataModel {
private long id;
private String content;
private String name;
public PatientDataModel()
{
}
public PatientDataModel(long id, String content, String name)
{
this.id = id;
this.content = content;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
#Override
public String toString()
{
return "Patient [id=" + id + ", firstName=" + name + ", " +
"content=" + content + "]";
}
}
another bean :
public class PatientDataModel1 {
private String address;
#Override
public String toString() {
return "Patient1 [address=" + address + "]";
}
public PatientDataModel1()
{
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
now , the issue is , I need both the beans to be accessible in the GET and POST method.
when I am running the code , it is executing but the beans does not have values , all are null . pls suggest
It will be easiest to have 1 object to find to the form. Create a new class PatientFormData for example that contains all the fields from the 2 objects and convert from/to the objects you have in the get and post methods in your controller.
For example:
public class PatientFormData {
private long id;
private String content;
private String name;
private String address;
public static PatientFormData from(PatientDataModel model,
PatientDataModel1 model1) {
id = model.getId();
content = model.getContent();
name = model.getName();
address = model.getAddress();
}
public PatientDataModel createPatientDataModel() {
PatientDataModel result = new PatientDataModel();
result.setId(id);
result.setContent(content);
result.setName(name);
return result;
}
// getters and setters here
}
Use this in the controller:
#Controller
public class PatientController {
ObjectMapper Obj = new ObjectMapper();
#GetMapping("/patient")
public static String patientForm(Model model) {
PatientFormData formData = PatientFormData.from(new PatientDataModel(), new PatientDataModel1());
model.addAttribute("patientFormData", formData);
return "patient";
}
#RequestMapping(value="/patient", method=RequestMethod.POST, params="action=Send data to MongoDB cluster")
public static String patientSubmit(#ModelAttribute("patientFormData") PatientFormData formData, Model model, Object obj ) throws JsonProcessingException {
PatientDataModel model = formData.createPatientDataModel();
PatientDataModel1 model1 = formData.createPatientDataModel1();
// Do more processing with objects
return "result";
}
Also be sure to correctly use the field binding using *{..}:
<form action="#" th:action="#{/patient}" th:object="${patientFormData}" method="post">
<p>Patient Id: <input type="text" th:value="*{id}" /></p>
<p>Patient Name: <input type="text" th:value="*{name}" /></p>
<p>Message: <input type="text" th:value="*{content}" /></p>
<p>address: <input type="text" th:value="*{address}" /></p>
</div>
<p><input type="submit" name="action" value="Send data to MongoDB cluster" />
<input type="reset" value="Reset" /></p>
</form>
</div>
Description of the problem
Spring boot cannot find the data sent in request body.
As specified below, in code extracts, I send form with application/x-www-form-urlencoded content-type to the endpoint POST /cards.
The good method is called by Spring boot but data from the request body aren't loaded in card entity, which is passed as parameter (see console output below).
Versions:
Spring boot: 2.3.4.RELEASE
spring-boot-starter-freemarker: 2.3.4.RELEASE
Console output (with request body read manually in request filter):
2020-10-21 00:26:58.594 DEBUG 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : New request method=POST path=/cards content-type=application/x-www-form-urlencoded
2020-10-21 00:26:58.595 DEBUG 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : RequestBody: title=First+card&seoCode=first-card&description=This+is+the+first+card+of+the+blog&content=I+think+I+need+help+about+this+one...
### createNewCard ###
card: Card<com.brunierterry.cards.models.Card#34e63b41>{id=null, seoCode='null', publishedDate=null, title='null', description='null', content='null'}
result: org.springframework.validation.BeanPropertyBindingResult: 0 errors
model: {card=Card<com.brunierterry.cards.models.Card#34e63b41>{id=null, seoCode='null', publishedDate=null, title='null', description='null', content='null'}, org.springframework.validation.BindingResult.card=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
2020-10-21 00:26:58.790 TRACE 38768 --- [nio-8080-exec-1] c.b.c.c.f.RequestResponseLoggingFilter : Response to request method=POST path=/cards status=200 elapsedTime=196ms
(Here I read body with req.getReader(), but I comment it usually to not consume the buffer.)
Controller
#Controller
public class CardController implements ControllerHelper {
#PostMapping(value = "/cards", consumes = MediaType.ALL_VALUE)
public String createNewCard(
#ModelAttribute Card card,
BindingResult result,
ModelMap model
) {
System.out.println("\n### createNewCard ###\n");
System.out.println("card: "+card);
System.out.println("result: "+result);
System.out.println("model: "+model);
return "/cards/editor";
}
#GetMapping(value = "/cards/form")
public String newPost(
Model model
) {
model.addAttribute("card", Card.defaultEmptyCard);
return "/cards/editor";
}
}
HTML form (wrote with freemarker template):
<form action="/cards"
method="POST"
modelAttribute="card"
enctype="application/x-www-form-urlencoded"
>
<div class="form-group">
<label for="title">Title & SEO slug code</label>
<div class="form-row">
<div class="col-9">
<#spring.formInput
"card.title"
"class='form-control' placeholder='Title'"
/>
<#spring.showErrors "<br>"/>
</div>
<div class="col-2">
<#spring.formInput
"card.seoCode"
"class='form-control' placeholder='SEO slug code' aria-describedby='seoCodeHelp'"
/>
<#spring.showErrors "<br>"/>
</div>
<div class="col-1">
<#spring.formInput
"card.id"
"DISABLED class='form-control' placeholder='ID'"
/>
</div>
</div>
<div class="form-row">
<small id="seoCodeHelp" class="form-text text-muted">
Keep SEO slug very small and remove useless words.
</small>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<#spring.formInput
"card.description"
"class='form-control' placeholder='Short description of this card..' aria-describedby='descriptionHelp'"
/>
<small id="descriptionHelp" class="form-text text-muted">
Keep this description as small as possible.
</small>
</div>
<div class="form-group">
<label for="content">Content</label>
<#spring.formTextarea
"card.content"
"class='form-control' rows='5'"
/>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
Card entity
#Entity
public class Card implements Comparable<Card> {
protected Card() {}
public static final Card defaultEmptyCard = new Card();
private final static Logger logger = LoggerFactory.getLogger(Card.class);
#Autowired
private ObjectMapper objectMapper;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#NotBlank(message = "Value for seoCode (the slug) is mandatory")
#Column(unique=true)
private String seoCode;
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate publishedDate;
#NotBlank(message = "Value for title is mandatory")
private String title;
#NotBlank(message = "Value for description is mandatory")
private String description;
#NotBlank(message = "Value for content is mandatory")
private String content;
public boolean hasIdUndefine() {
return null == id;
}
public boolean hasIdDefined() {
return null != id;
}
public Long getId() {
return id;
}
public String getSeoCode() {
return seoCode;
}
public LocalDate getPublishedDate() {
return publishedDate;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getContent() {
return content;
}
private String formatSeoCode(String candidateSeoCode) {
return candidateSeoCode.replaceAll("[^0-9a-zA-Z_-]","");
}
private Card(
#NonNull String rawSeoCode,
#NonNull String title,
#NonNull String description,
#NonNull String content,
#NonNull LocalDate publishedDate
) {
this.seoCode = formatSeoCode(rawSeoCode);
this.title = title;
this.description = description;
this.content = content;
this.publishedDate = publishedDate;
}
public static Card createCard(
#NonNull String seoCode,
#NonNull String title,
#NonNull String description,
#NonNull String content,
#NonNull LocalDate publishedDate
) {
return new Card(
seoCode,
title,
description,
content,
publishedDate
);
}
public static Card createCard(
#NonNull String seoCode,
#NonNull String title,
#NonNull String description,
#NonNull String content
) {
LocalDate publishedDate = LocalDate.now();
return new Card(
seoCode,
title,
description,
content,
publishedDate
);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Card card = (Card) o;
return Objects.equals(id, card.id) &&
seoCode.equals(card.seoCode) &&
publishedDate.equals(card.publishedDate) &&
title.equals(card.title) &&
description.equals(card.description) &&
content.equals(card.content);
}
#Override
public int hashCode() {
return Objects.hash(id, seoCode, publishedDate, title, description, content);
}
#Override
public String toString() {
return "Card<"+ super.toString() +">{" +
"id=" + id +
", seoCode='" + seoCode + '\'' +
", publishedDate=" + publishedDate +
", title='" + title + '\'' +
", description='" + description + '\'' +
", content='" + content + '\'' +
'}';
}
public Either<JsonProcessingException,String> safeJsonSerialize(
ObjectMapper objectMapper
) {
try {
return Right(objectMapper.writeValueAsString(this));
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return Left(e);
}
}
public Either<JsonProcessingException,String> safeJsonSerialize() {
try {
return Right(objectMapper.writeValueAsString(this));
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return Left(e);
}
}
#Override
public int compareTo(#NotNull Card o) {
int publicationOrder = this.publishedDate.compareTo(o.publishedDate);
int defaultOrder = this.seoCode.compareTo(o.seoCode);
return publicationOrder == 0 ? defaultOrder : publicationOrder;
}
}
Edit
I got a good answer.
It works when adding empty constructor and setters to the Card entity.
However, it's not the class I want.
I want card to be only instantiated with a constructor that have all parameters.
Do you have an idea about how to achieve that ?
Should I create another class to represent the form ?
Oris there a way to only allow Spring to use such setters ?
Did you make sure that you Card.java has the appropriate getters and setters? This way spring can actually populate the data in the object it is trying to create.
This is the full error that I get in the console:
"Could not read document: Can not deserialize value of type int from
String "${product.id}": not a valid Integer value↵ at [Source:
java.io.PushbackInputStream#40d1a098; line: 1, column: 14] (through
reference chain: haughton.dvdstore.model.AddToCartPojo["productId"]);
nested exception is
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not
deserialize value of type int from String "${product.id}": not a valid
Integer value↵ at [Source: java.io.PushbackInputStream#40d1a098; line:
1, column: 14] (through reference chain:
haughton.dvdstore.model.AddToCartPojo["productId"])"
My html
<form method="post">
<p>Enter quantity you would like to purchase :
<input type="number" id="quantity" name="quantity" step="any" min="1" max="${product.quantityInStock}" value="1"></input>
</p>
<input type="submit" class="btn btn-primary" id="addToCart" name="button" value="Add to cart"/>
<input type="hidden" id="productId" value='${product.id}'/>
</form>
App.js
$("#addToCart").click(function(event) {
var data = {}
data["productId"] = $("#productId").val();
data["quantity"] = $("#quantity").val();
$.ajax({
type: "POST",
contentType: "application/json",
url: "http://localhost:8080/addToCart",
data: JSON.stringify(data),
dataType: 'json',
timeout: 600000,
success: function (data) {
console.log(data);
//...
},
error: function (e) {
//...
}
});
event.preventDefault();
});
Controller
#Controller
#Scope("session")
public class CartController {
#Autowired
private Cart cart;
#Autowired
ProductService productService;
#RequestMapping(value="/cart", method= RequestMethod.GET)
public String searchResults(Model model) {
model.addAttribute("cartLines",cart.getLines());
model.addAttribute("cartTotalPrice",cart.getTotalPrice());
return "cart";
}
#ResponseBody
#RequestMapping(value="/addToCart", method= RequestMethod.POST)
public String searchResults(#RequestBody AddToCartPojo addToCartPojo) {
Product product = productService.findById(((long) addToCartPojo.getProductId()));
if(product == null){
//if the productid supplied doesnt belong to a product in our database
return "failure";
}
FlashMessage result = cart.add(product,addToCartPojo.getQuantity());
return result.getStatus().toString();
}
AddToCartPojo
//pojo for sending via ajax
public class AddToCartPojo {
private int productId;
private int quantity;
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
I would suggest altering your AddToCartPojo so that productId is a String instead of an int:
So change this :
private int productId;
To this :
private String productId;
You'll need to change your getters 'n' setters too.
I have a local Oracle database with which I've established a solid connection. Now I just want to POST data from an HTML input form using a Controller to handle said data.
My form looks like this:
<form action="/request/save" method="post">
<input type="text" id="dateInput" name="requestDate" value="1" style="display: none;"/>
<input type="text" name="description" value="This is a test request." style="display: none;"/>
<input type="text" name="status" value="false" style="display: none;"/>
<div style="width: 200px;"><input type="submit" value="Submit Request" style="display: block;"></div>
</form>
Controller:
#RequestMapping(value = "/save", method = RequestMethod.POST)
String saveRequest(Principal principal, #ModelAttribute Request request, Model model) {
// Set UserId to Request Field USER_ID
Users user = usersRepository.findOneByInitialName(principal.getName());
Request requestObj = new Request(user, new Date());
requestObj.setId(user.getId());
// Set Additional Request Fields
requestObj.setDescription("Test");
requestObj.setStatus(false);
requestObj.setRequestDate(new Date());
// Save Request Object
requestRepository.save(requestObj);
return "requests";
}
Entity (for completion):
#Entity
public class Request {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="request_id")
private Long id;
private Date requestDate;
private String description;
private Boolean status;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id", nullable = false)
private Users users;
public Request() {}
public Request(Users user, Date requestDate) {
this.setUsers(user);
this.setRequestDate(requestDate);
}
#Override
public String toString() {
return String.format(
"Request[id=%d, inital='%s', requestDate='%s']",
getId()
, getUsers().getInitialName()
, getRequestDate());
}
public Date getRequestDate() {
return requestDate;
}
public void setRequestDate(Date requestDate) {
this.requestDate = requestDate;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Users getUsers() {
return users;
}
public void setUsers(Users users) {
this.users = users;
}
}
How can I send the data from the form to my database?
Error:
Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
You might have configured csrf filter in your web.xml, so you need to pass "X-CSRF-TOKEN" in your request header.