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.
CodePudding user response:
You can use ObjectMapper
to fetch the desired key-value from the @RequestBody String executor
as:
Approach Here:
The @RequestBody
that you are getting from the API is in the object form and using ObjectMapper
, it will be converted into a Map of attributes as in the API request and we can fetch the desired key-value pair.
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> map = mapper.readValue(executor, new TypeReference<>() {});
String s = (String) map.get("executor");
Added in acceptAssignment method as:
@Override
public String acceptAssignment(Integer id, String executor) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Map<String,Object> requestMap = mapper.readValue(executor, new TypeReference<>() {});
Assignment assignment = assignmentRespository.findById(id).orElse(null);
assignment.setExecutor((String)requestMap.get("executor"));
AssignmentDTO assignmentDTO = assignmentConverter.convertEntityToDto(assignment);
assignmentRespository.save(assignment);
return assignmentDTO.getExecutor();
}
CodePudding user response:
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 tread 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 static 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 in case if the data that corresponds to the givenid
was not found instead ofNullPointerException
, which would be triggered on the next line. 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;
}