Spring Controller throws NullPointerException when Pass Integer Data With Ajax/Post - ajax

I'm trying to pass set of data in html form;
<form class="form-inline" id="createjobform" method="POST" th:action="#{/createJob}">
...
<div class="form-group">
<input type="text" class="form-control" id="nopth"
placeholder="Number Of People To Hire" name="nopth" />
</div>
<div class="form-group">
<input type="text" readonly="readonly" class="form-control"
id="listid" placeholder="ID" name="listid"
title="ID of the list which associated"
th:value="${findOneList.id}"/>
</div>
listid in here, coming from another table (manytoone-onetomany) and I want to add new record with this listid. When I do it on phpmyadmin, it's working. But I want to do it with ajax post request or not without ajax, no matter actually. I tried both ways but it shows same error.
Here is my controller;
#RequestMapping(value = "/createJob", method = RequestMethod.POST)
public #ResponseBody void createJob(#RequestBody Jobs jobs,
#RequestParam(value = "title", required = false) String title,
#RequestParam(value = "description", required = false) String description,
#RequestParam(value = "nopth", required = false) Integer nopth,
#RequestParam(value = "lastDate", required = false) Date lastDate,
#RequestParam(value = "listid", required = false) Long listid,
HttpServletResponse hsr) throws IOException {
// if I do String nopth above and then
//jobs.setNopth(Integer.valueOf(nopth));
// error is NumberFormatException (cannot cast string to int)
jobs.setLastDate(lastDate);
jobs.setTitle(title);
jobs.setDescription(description);
jobs.setNopth(nopth);
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
jobsService.save(jobs);
mavHomepage.addObject("findOneList", listsService.findOne(jobs.getId()));
mavHomepage.addObject("mod", "VIEW_ONELIST");
hsr.sendRedirect("/oneList?id=" + listid);
}
so error is;
error: "Internal Server Error"
exception: "java.lang.NullPointerException"
message: "No message available"
path: "/createJob"
status: 500
at line jobs.setNopth(nopth);
also error is;
error: "Internal Server Error"
exception: "org.springframework.dao.InvalidDataAccessApiUsageException"
message: "The given id must not be null!; nested exception is
java.lang.IllegalArgumentException: The given id must not be null!"
path: "/createJob"
status: 500
at line Lists listttt = listsService.findOne(listid);
This is not with ajax/post. When I do;
public #ResponseBody void createJob(#RequestBody Jobs jobs,
#RequestParam(value="listid, required="false") listid,
HttpServletResponse hsr){
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
...
}
and ajax;
var formData = {
title : $("#title").val(),
description : $("#description").val(),
nopth : $("#nopth").val(),
lastDate : $("#lastDate").val(),
listid : $("#listid").val(),
}
...
$.ajax({
type : "POST",
contentType : "application/json",
url : "/createJob",
data : JSON.stringify(formData),
....
same error (The given id must not be null!)
SO HOW MUST I PASS DATA FROM FORM WHICH IS INTEGER/LONG VALUES ? WITH AJAX WOULD BE BETTER. AND THAT listid is FOREIGN KEY.
Model Classes;
#Entity(name = "jobs")
public class Jobs {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "Title")
private String title;
#Column(name = "Description")
private String description;
#Column(name = "Number_Of_People_To_Hire")
private Integer nopth;
#Column(name = "Last_Application_Date")
private Date lastDate;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="listid")
private Lists lists;
...
#Entity(name = "lists")
public class Lists implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#Column(name = "List_Name")
public String listname;
#OneToMany(mappedBy = "lists", cascade = CascadeType.ALL)
private List<Jobs> jobs;
I've tried to change type of nopth to String, Integer etc. and input type="text or number" no changes.

#RequestBody This annotation indicates a method parameter should be bound to the body of the web request
So you have to fix your controller like this:
#RequestMapping(value = "/createJob", method = RequestMethod.POST)
#ResponseBody
public void createJob(
#RequestBody Jobs jobs,
#RequestParam("listid") Long listid
HttpServletResponse hsr) throws IOException {
Lists listttt = listsService.findOne(listid);
jobs.setLists(listttt);
jobsService.save(jobs);
...
}
Your jquery request:
var formData = {
title : $("#title").val(),
description : $("#description").val(),
nopth : $("#nopth").val(),
lastDate : $("#lastDate").val()
}
var listid : $("#listid").val(),
var settings = {
"url": "http://localhost:8080/createJob?listid="+listid,
"method": "POST",
"data": JSON.stringify(formData)
"headers": {
"Content-Type": "application/json"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});

Related

Cannot convert value of type MultipartFile to String

When I added validation for field Name I got an error:
Validation failed for object='item'. Error count: 1org.springframework.validation.BindException
Field error in object 'item' on field 'image': rejected java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'image': no matching editors or conversion strategy found
Entity, class Item
#Entity
#Table(name = "items")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#NotBlank(message = "Введите наименование")
#Column(name = "name")
private String name;
#JsonIgnore
#Lob
#Column(name = "image")
private String image;
}
Main controller
#PostMapping("/items")
public String add(
#Valid Item item,
#RequestParam("image") MultipartFile file,
BindingResult bindingResult,
Model model
) throws IOException {
if (bindingResult.hasErrors()){
Map<String, String> errorsMap = ControllerUtils.getErrors(bindingResult);
model.mergeAttributes(errorsMap);
model.addAttribute("item", item);
} else {
if (file != null && !file.getOriginalFilename().isEmpty()) {
byte[] data = file.getBytes();
String imageString = Base64.getEncoder().encodeToString(data);
item.setImage(imageString);
}
model.addAttribute("item", null);
itemService.saveItem(item);
}
I solved the problem by creating new formParams (quite similar with entity Items with all validation parameters) and put this form as a parameter to the post method.
#PostMapping("/items/save")
public String add(#AuthenticationPrincipal User user, #Valid ItemInputParams formParams,
BindingResult bindingResult, Model model) throws IOException {
if (bindingResult.hasErrors()){
Map<String, String> errorsMap =
ControllerUtils.getErrors(bindingResult);
model.mergeAttributes(errorsMap);
model.addAttribute("item", formParams);
return initItems(null, model);
}
Item item = new Item();
item.setName(formParams.getName());
(...)
MultipartFile file = formParams.getImage();
if (file != null && !file.getOriginalFilename().isEmpty()) {
byte[] data = file.getBytes();
String imageString = Base64.getEncoder().encodeToString(data);
item.setImage(imageString);
}
(...)
}
Ensure you have an enctype in your form opening tag and also ensure the name in the request param is the same name in the form and in the database field

Thymeleaf Spring Validation doesn't display

I am trying display error message on HTML which i got from Controller, but i dont see the message displaying.
I printed the ${#fields.hasErrors('name')} and i see it give me false
I debugged my code, and I found there is an error
com.gurnah.controller.ProductController : product size must be between 2 and 150
#RequestMapping(path = "/product")
public class ProductController {
private static final Logger logger = LoggerFactory.getLogger(ProductController.class);
#Autowired
private ProductRepo productRepo;
.....
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(#ModelAttribute #Valid Product product, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute("product", new Product());
// logger.info("Error Message " + bindingResult.getGlobalErrorCount());
logger.info("Error Message " + errors.getFieldErrorCount());
List<ObjectError> err = errors.getAllErrors();
for (ObjectError e : err) {
logger.info(e.getObjectName() + e.getDefaultMessage());
}
// logger.info("Error Message " + bindingResult.getErrorCount());
return "/prod/create";
} else {
productRepo.save(product);
return "redirect:/product/list";
}
}
My Pojo
#Entity
public class Product {
#Id
#Column(name = "ProdID", updatable = false, nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_generator")
#SequenceGenerator(name="product_generator", sequenceName = "zseq_product", allocationSize=1)
private int ProdID;
#NotNull(message = "Field Product cannot be empty")
// #NotEmpty(message = "Field Product cannot be empty")
#Size(min = 2, max = 150)
private String name;
private String prodDesc;
My HTML
<form th:object="${product}" th:action="#{/product/save}" method="post">
.....
<div class="form-group"
th:classappend="${#fields.hasErrors('name')}? 'has-error'">
<label th:for="name" class="col-form-label">Product Name:</label>
<input type="text" class="form-control" th:field="*{name}" />
<span th:if="${#fields.hasErrors('name')}"
th:errors="*{name}"
th:class="help-block">Title Errors</span>
</div>

Use #RequestBody to receive data from ajax and binding it to 'User', but failed

I used AJAX to submit data to the spring boot backend with the #RequestBody annotation to accept it. However, when doing that, it showed the followinng error, which confuses me:
Required String parameter 'username' is not present
Console output
Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'username' is not present]
AJAX code
$("#submitBTN").click(
function(){
$.ajax({
type:"post",
async:false,
url:"/user/doSignIn?verification="+$("input[name='verificationCode']").val(),
contentType: "application/json;charset=utf-8",//必须加
data: JSON.stringify({
'username': $("input[name='username']").val(),
'password':$("input[name='password']").val(),
'email': $("input[name='email']").val()
}),
success:function(r){
if(r.code==window.ResponseStatus.OK){
$(window).attr('location',+'/index');
}else{
console.log(r.msg);
}
}
});
}
);
Entity User
#Entity
public class User {
// 自增id
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// 用户名
#Pattern(regexp = "^.{4,14}$",groups = UserSignUp.class)
#Column(length = 14)
private String username;
// 密码
#Pattern(regexp = "^[0-9a-zA-Z!##$%^&*.]{6,25}$")
#Column(length = 25)
private String password;
// 邮箱
#Email
private String email;
// 电话号码
#Pattern(regexp = "^\\d{11}$",groups = UserSignUp.class)
#Column(length = 11)
private String phoneNumber;
// 所属用户组
private byte userGroup;
RequestHandler code
#PostMapping(value = "/doSignUp")
public FOResponse doSignUp(#RequestBody User user,
BindingResult result,
#RequestParam String verificationCode,
HttpSession session){...}

Spring MVC Error: Failed to convert property value of type java.lang.String to required type

I can't let this exception go:
Failed to convert property value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId; nested exception is java.lang.IllegalStateException: Cannot convert value of type java.lang.String to required type com.company.springdemo.entity.Product for property productId: no matching editors or conversion strategy found
Order Model
#Entity
#Table(name = "orders") // naming the table only order, will throw exception
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private Integer orderId;
#OneToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "product_id")
private Product productId;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
#JoinColumn(name = "client_id")
private Client client;
....
Product Model
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Integer id;
#Column(name = "product_name")
private String productName;
#Column(name = "product_serial")
private String productSerial;
...
Client Model
#Entity
#Table(name = "clients")
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotEmpty
#Column(name = "first_name")
private String firstName;
#NotEmpty
#Column(name = "last_name")
private String lastName;
#NotEmpty
#Email
#Column(name = "email")
private String email;
#NotEmpty
#Column(name = "location")
private String location;
#OneToMany(mappedBy = "client",cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Order> orders;
Controller, where I save the order with related client and product
#PostMapping("add")
public ModelAndView addOrder( #Validated #ModelAttribute("ords") Order order, BindingResult bindingResult ){
if (bindingResult.hasErrors()) {
System.out.println("Having errors: " + bindingResult.getAllErrors());
Iterable<Product> products = productService.listProducts();
Iterable<Client> clients = clientService.listClients();
System.out.println("Error "+ bindingResult.getAllErrors());
ModelAndView mv = new ModelAndView("orders/add-order");
mv.addObject("products",products);
mv.addObject("clients",clients);
return mv;
}
try {
orderService.saveOrder(order);
} catch (Exception e) {
e.printStackTrace();
}
ModelAndView mv = new ModelAndView("redirect:list");
return mv;
}
Finally, my JSP form View page
<form:form action="add" method="post" modelAttribute="ords">
<label for="productId" >Product Id</label>
<form:select path="productId" >
<c:forEach var="product" items="${products}">
<form:option value="${product.id}">${product.productName}</form:option>
</c:forEach>
</form:select>
<form:errors path="productId"/>
<br>
<label for="client" >Client Id</label>
<form:select path="client" >
<c:forEach var="client" items="${clients}">
<form:option value="${client.id}">${client.id} - ${client.lastName}</form:option>
</c:forEach>
</form:select>
<form:errors path="client"/>
<br>
<input type="submit" value="Place Order">
</form:form>
What am I doing wrong?
You most likely need to build a converter class such as this one :
#Component("facilityConverter")
public class FacilityConverter implements Converter<String, Facility>
{
#Autowired
FacilityService facilityService;
#Override
public Facility convert(String id)
{
return facilityService.findById(Integer.parseInt(id));
}
}
Then, you need to register it by implementing the addFormatters method inside of a configuration class implementing WebMvcConfigurer like so :
#Override
public void addFormatters (FormatterRegistry registry)
{
registry.addConverter((FacilityConverter)ctx.getBean("facilityConverter"));
}
Your entities will then correctly be mapped from a dropdown selection. Also, this might not be part of your issue but you can just build your dropdowns like this :
<form:select name="linkedInterface" path="linkedInterface" id="linkedInterface">
<form:options items="${interfaces}" itemLabel="name" itemValue="id"/>
</form:select>
The productId field is actually a Product object, not an ID (String/int). You need your JSP to use path="productId.id" rather than path="productId".
(Although I'd also suggest you also rename the field product rather than productId.)
<form:select path="product.id">
I think you'll hit the same issue on your <form:select path="client"> too.

Spring MVC 3.1 : how to map JSON from a PUT request body?

I know this question has been asked a gazillion times, but I still cannot find a solution to my problem, which basically boils down to JSON deserialization from a PUT request.
I've already added HiddenHttpMethodFilter as a filter.
org.codehaus.jackson.jackson-mapper-lgpl is in the classpath.
Here is the client part:
$.ajax({
url: '/occurrence',
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify({id:id,startDate:startDate, endDate:endDate, frequencyType:frequency})
})
Here is the controller part:
#Controller
#RequestMapping("/occurrence")
public class OccurrenceController {
private static final String COMMAND = "eventCommand";
#Autowired
private PersistenceCapableOccurrence occurrenceDao;
#Autowired
private PersistenceCapableFrequencyType frequencyTypeDao;
#InitBinder(COMMAND)
public void customizeConversions(final WebDataBinder binder) {
DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm");
df.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(df, true));
EntityConverter<FrequencyType> frequencyTypeEntityConverter = new EntityConverter<FrequencyType>(frequencyTypeDao, FrequencyType.class, "findByValue", String.class);
((GenericConversionService) binder.getConversionService()).addConverter(frequencyTypeEntityConverter);
}
#RequestMapping(method = PUT, consumes = "application/json")
#ResponseBody
public Long saveOccurrence(#RequestBody Occurrence occurrence) {
return occurrenceDao.saveOrUpdate(occurrence);
}
}
Here are my two domain classes (Occurrence and FrequencyType):
public class Occurrence {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", nullable = false)
private long id;
#NotNull
#Column(name = "start_date")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime startDate;
#Column(name="end_date")
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime endDate;
#ManyToOne
#JoinColumn(name = "frequency_type", nullable = false)
private FrequencyType frequencyType;
/* C-tor (1 with [start,end,freq], another with [start,freq]), getters (no setters) */
}
#Entity
#Table(name = "frequency_types")
public class FrequencyType {
public enum FrequencyTypeValues {
ONCE, DAILY, WEEKLY, MONTHLY, YEARLY;
}
private String value;
public FrequencyType() {}
public FrequencyType(FrequencyTypeValues value) {
this.value = value.name();
}
#Id
#Column(name = "value")
public String getValue() {
return value;
}
public void setValue(String value) {
//validates value against the enumerated/allowed values (ie throws exceptions if invalid value)
FrequencyTypeValues.valueOf(value.toUpperCase());
this.value = value;
}
}
All I get at the end is a 400 response.
Example :
PUT Request
{"id":"","startDate":"20/10/2012 17:32","endDate":"","frequencyType":"YEARLY"}
Response
"NetworkError: 400 Bad Request - http://localhost:9999/occurrence"
Thanks in advance for your help !
Rolf

Resources