Issue
I'm receiving an id
(integer) and a executor
(String) in my controller (Rest API). However, when looking at my database, I see that the string is being inserted into the database as an object. Example of database entry:
{
"executor": "Pietje"
}
Controller:
@PostMapping(path = "/accept/{id}")
public String acceptAssignment(@Valid @PathVariable Integer id, @RequestBody String executor) {
return assignmentService.acceptAssignment(id, executor);
}
Service implementation:
@Override
public String acceptAssignment(Integer id, String executor) {
Assignment assignment = assignmentRespository.findById(id).orElse(null);
assignment.setExecutor(String.valueOf(executor));
AssignmentDTO assignmentDTO = assignmentConverter.convertEntityToDto(assignment);
assignmentRespository.save(assignment);
return assignmentDTO.getExecutor();
}
What am I doing wrong, and how can I fix it?
I could pass along the entire DTO instead of just the 'executor' value, but that doesn't seem efficient. As far as I know, the problem is not with the frontend but I could add the React code if necessary.
Solution
TL;DR - you're using a String
containing a JSON-object as if it was an attribute of this JSON-object. The solution it to treat this JSON properly.
Note that you don't need to mess with desirialization manually, let the JSON-converter of the framework do its job.
All that you need is a simple POJO:
@Getter
@Setter
public class AssignmentExecutor {
private String executor;
}
The above POJO can be automatically translated in & to the following of JSON without any effort from your side (owing to the magic of Spring):
{
"executor": "Pietje"
}
It would be automatically parsed to the proper type by a Spring's message-converter, you only need to specify that you need an AssignmentExecutor
instead of a plain String
.
@PostMapping(path = "/accept/{id}")
public String acceptAssignment(@Valid @PathVariable Integer id,
@RequestBody AssignmentExecutor executor) {
return assignmentService.acceptAssignment(id, executor);
}
Note
- Introducing this new type would not require any changes in the
Assignment
, executor can still be represented as aString
field. - By invoking
orElse(null)
on the optional result, you're creating a potential problem by depriving the possibility to get a meaningful exception if the data that corresponds to the givenid
was not found. In such a case, your current code would trigger aNullPointerException
right on the next line. Instead, I would advise providing a suitable exception viaOptional.orElseThrow()
.
A now again all that you need is to return an instance of AssignmentExecutor
and it would be automatically converted into JSON:
@Override
public String acceptAssignment(Integer id, AssignmentExecutor executor) {
Assignment assignment = assignmentRespository.findById(id)
.orElseThrow(() -> new MyException("Assignment with id " + id + " was not found"));
assignment.setExecutor(executor.getExecutor());
assignmentRespository.save(assignment);
return executor;
}
Answered By - Alexander Ivanchenko
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)