Cannot convert value of type MultipartFile to String - spring

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

Related

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

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);
});

how not to consider #NotBlank in some methods

I'm doing a restful app in Spring boot,jpa,mysql. I have annoted some of my model fields #NotBlank to print an error in the creation of an object if those fields are blank.
Now when i'm updating, I don't want to get that error if I don't set some fields in my json body.My goal is to update just the fields which are present.
So I want to know if there is a way not to consider an #NotBlank in my updating method.
This is the code source :
For the Entity
public class Note implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(name)
private String title;
#NotBlank
private String content;
//Getters and Setters
}
The controller
#RestController
#RequestMapping("/api")
public class NoteController {
#Autowired
NoteRepository noteRepository;
// Create a new Note
#PostMapping("/notes")
public Note createNote(#Valid #RequestBody Note note) {
return noteRepository.save(note);
}
// Update a Note
#PutMapping("/notes/{id}")
public Note partialUpdateNote(#PathVariable(value = "id") Long noteId,
#RequestBody Note noteDetails) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
//copyNonNullProperties(noteDetails, note);
if(note.getTitle()!= null) {
note.setTitle(noteDetails.getTitle());
}else {
note.setTitle(note.getTitle());
}
if(note.getContent()!= null) {
note.setContent(noteDetails.getContent());
}else {
note.setContent(note.getContent());
}
Note updatedNote = noteRepository.save(note);
return updatedNote;
}
// Delete a Note
#DeleteMapping("/notes/{id}")
public ResponseEntity<?> deleteNote(#PathVariable(value = "id") Long noteId) {
Note note = noteRepository.findById(noteId)
.orElseThrow(() -> new ResourceNotFoundException("Note", "id", noteId));
noteRepository.delete(note);
return ResponseEntity.ok().build();
}
}
ResourceNotFoundException is the class responsible to throws errors.
You can use groups for that.
Add two interfaces CreateGroup and UpdateGroup.
Use them by this way:
#NotBlank(groups = CreateGroup.class)
#Null(groups = UpdateGroup.class)
private String title;
In the create endpoint
#Valid #ConvertGroup(from = Default.class, to = CreateGroup.class) Note note
In the update endpoint
#Valid #ConvertGroup(from = Default.class, to = UpdateGroup.class) Note note
Probably you don't need UpdateGroup. It is just to show a common approach.
Also for the nested objects inside Note something like
#ConvertGroup(from = CreateGroup.class, to = UpdateGroup.class)
can be used.

marshall attributes inside XML elements with JAXB

I work with Spring JPA and have the following entity:
#Entity
#Table(name = Constants.ENTITY_TABLE_PREFIX + "ENTRY")
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "monObj_info")
public class EntryXML implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
#XmlAttribute
private long id;
#Column(name = "ip_address", nullable = true)
#XmlElement
private String ip_address;
#Column(name = "network_element_name", nullable = false)
#XmlElement
private String network_element_name;
public EntryXML() {}
public EntryXML(long id, String ip_address, String network_element_name) {
super();
this.id = id;
this.ip_address = ip_address;
this.network_element_name = network_element_name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getIp_address() {
return ip_address;
}
public void setIp_address(String ip_address) {
this.ip_address = ip_address;
}
public String getNetwork_element_name() {
return network_element_name;
}
public void setNetwork_element_name(String network_element_name) {
this.network_element_name = network_element_name;
}
}
and the endpoint:
#RestController
public class EntryXMLEndpoint {
#Autowired
private IEntryXMLService service;
#RequestMapping(value = "/restxml", produces = { "application/xml" })
public EntryXML findEntries() {
EntryXML record = service.findById(1);
return record;
}
}
Now the requested response is:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>
Of course what I get is :
<monObj_info id="1">
<ip_address>xx.xxx.xxx.x</ip_address>
<network_element_name>xxxxxx</network_element_name>
</monObj_info>
I read similar posts , but the problem is I cannot create a List with the required elements inside my Entity Class, since it will not map with any column in the respective table, any suggestions?
You can achieve your goal in a straight-forward but somewhat hackish way.
Since you don't want the ip_address and network_element_name properties
to be marshalled and unmarshalled directly, you need to remove their #XmlElement annotation
and add #XmlTransient.
Instead, you want some <atribute name="..." value="..." /> elements marshalled and unmarshalled.
Therefore you need to add the following things to your EntryXML class:
an attributes property holding a list of attributes.
It is annotated with #XmlElement so that it will be part of XML marshalling and unmarshalling.
It is annotated with #Transient so that it will not be part of database persistence.
a simple helper class Attribute for holding name and value.
name and value are annotated with #XmlAttribute so that they will be part of XML marshalling and unmarshalling.
a Marshal Event Callback (beforeMarshal)
for doing the conversion from ip_address and network_element_name
to the attributes list.
an Unmarshal Event Callback (afterUnmarshal)
for doing the opposite conversion.
#XmlElement(name = "atribute")
#Transient // from package javax.persistence
private List<Attribute> attributes;
// there is no need for getAttributes and setAttributes methods
private static class Attribute {
#SuppressWarnings("unused") // called by the unmarshaller
Attribute() {
}
Attribute(String name, String value) {
this.name = name;
this.value = value;
}
#XmlAttribute
private String name;
#XmlAttribute
private String value;
}
#SuppressWarnings("unused") // this method is called only by the marshaller
private boolean beforeMarshal(Marshaller marshaller) {
attributes = new ArrayList<>();
attributes.add(new Attribute("ip_address", ip_address));
attributes.add(new Attribute("network_element_name", network_element_name));
return true;
}
#SuppressWarnings("unused") // this method is called only by the unmarshaller
private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
if (attributes != null) {
for (Attribute attribute : attributes) {
switch (attribute.name) {
case "ip_address":
ip_address = attribute.value;
break;
case "network_element_name":
network_element_name = attribute.value;
break;
}
}
}
}
Then the XML output will look like this:
<monObj_info id="1">
<atribute name="ip_address" value="xx.xxx.xxx.x"/>
<atribute name="network_element_name" value="xxxxxx"/>
</monObj_info>

Spring - Many To One - phonebook assigning contacts to user

I am a newbee in java and spring. My first exercise is project to simulate web phone book. I'm stuck with assigning contact to specific user, and later on displaying contacts for that specific user. Any idea or guideline is appreciated.
User class
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String lastName;
private String email;
private String username;
private String password;
#Transient
private String retypePassword;
#ManyToMany(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinTable(name="users_roles",
joinColumns = {#JoinColumn(name="user_id", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="role_id", referencedColumnName="roles_id")}
)
private List<Rolee> authorities;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "userForPhonebook")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Contact> allContacts;
// getters and setters
Contact class
#Entity
public class Contact implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int contactID;
private String name;
private String lastName;
private String email;
private String areaCode;
private String telNumber;
private String adress;
private String city;
private String note;
private String contactImage;
#JoinColumn(name = "users_id")
#ManyToOne(fetch = FetchType.EAGER, optional = false)
private User userForPhonebook;
// getters and setters
ContactServiceImpl class
#Service
public class ContactServiceImpl implements ContactService {
#Autowired
private ContactDAO contactDAO;
#Autowired
private UserDAO userDAO;
#Autowired
private ServletContext context;
#Override
public void addContact(ContactModel contactModel, MultipartFile[] contactImages, User user) {
Contact contact = new Contact();
/*
creating contact
*/
User user2 = userDAO.userGetById(user.getId()); //<-- i cant get user id
contact.setUserForPhonebook(user2);
// --------------------------------
User u = new User(); //
u.setId(2); // hard coding users id...
contact.setUserForPhonebook(u); // and its working fine
// rest of code to create contact ...
UserDAOImpl
// ...
#Override
public void addUser(User user) {
Session s = getCurrentSession();
Transaction trans = s.beginTransaction();
getCurrentSession().save(user);
trans.commit();
}
// ...
UserServiceImpl
// ...
#Override
public void addUser(UserModel userModel) {
User user = new User();
// creating user ...
getUserDAO().addUser(user);
}
// ...
RegisterController
// ...
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String postRegister(#Valid #ModelAttribute("newUser") UserModel userModel, BindingResult results, ModelMap model) {
if (results.hasErrors()) {
return "addUser";
}
// checking if username, email exist in database ...
// password matching
getUserService().addUser(user);
return "index";
}
// ...
ContactController
// ...
#RequestMapping(value = "/addContact", method = RequestMethod.POST)
public String postAddContact(#Valid #ModelAttribute("addContact") ContactModel contactModel, BindingResult results, HttpServletRequest request, #RequestParam("contactImages") MultipartFile[] contactImages, User user)
throws FileNotFoundException, IOException {
if (results.hasErrors()) {
return "addContact";
}
getContactService().addContact(contactModel, contactImages, user);
return "redirect:phoneBook";
}
//...
You are not specifying how Spring is supposed to bind your User object in your postAddContact signature.
#RequestMapping(value = "/addContact", method = RequestMethod.POST)
public String postAddContact(
// Ok, this is a model retrieved from request params
#Valid #ModelAttribute("addContact") ContactModel contactModel,
// Ok, a BindingResult is mapped when the validation above occurs
BindingResult results,
// Ok, bind the internal HttlServletRequest
HttpServletRequest request,
// Ok, bind this to the multipart part of the request
#RequestParam("contactImages") MultipartFile[] contactImages,
// ... no idea how to bind this
User user)
throws FileNotFoundException, IOException {
// ...
}
You need to specify yourself which user is going to get the contact.
You could add the field inside your ContactModel object, like userId, and in your controller retrieve that user from database before adding the contact.
ContactController.java
// ...
#RequestMapping(value = "/addContact", method = RequestMethod.POST)
public String postAddContact(#Valid #ModelAttribute("addContact") ContactModel contactModel, BindingResult results, HttpServletRequest request, #RequestParam("contactImages") MultipartFile[] contactImages)
throws FileNotFoundException, IOException {
if (results.hasErrors()) {
return "addContact";
}
// Retrieve the user
User user = getUserService().retrieveUser(contactModel.getUserId());
getContactService().addContact(contactModel, contactImages, user);
return "redirect:phoneBook";
}
//...
You could also add a path variable, use the connected user, etc. Above code is just a suggestion.
The answer here helped me to assign currently active user to contact and my postAddContact looks like this. Sorry if I didn't correctly ask the question and I hope this will help someone else
#RequestMapping(value = "/addContact", method = RequestMethod.POST)
public String postAddContact(#Valid #ModelAttribute("addContact") ContactModel contactModel, BindingResult results, HttpServletRequest request, #RequestParam("contactImages") MultipartFile[] contactImages)
throws FileNotFoundException, IOException {
if (results.hasErrors()) {
return "addContact";
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserDetails userDetail = (UserDetails) auth.getPrincipal();
User u = userDAO.getUserByUsername(userDetail.getUsername());
request.getSession().setAttribute("id", u.getId());
int userId = (int) request.getSession().getAttribute("id");
User user = new User();
user.setId(userId);
getContactService().addContact(contactModel, contactImages, user);
return "redirect:phoneBook";
}

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