'Body of ResponseEntity is null even though the object given in its constructor is not null
Alright, so I got two APIs. One is doing persistance only (let's call it 'A') and the other one is some kind of calculator ('B').
What I am trying to do is, that if you call a @GetMapping-method in B, that B will get the data with the given ID from A, maps the JSON into a Java object, does all the calculation and when the calculation is done, post the result to A.
All that with one Request.
Everything works fine until I want to post the result to A. In this case, I have an object of my class "TcoResult" as a parameter for the "post-method". When I call the to-String on that object in this very method, I get all the values it has and everything is fine, but if I return it as body of a ResponseEntity, that ResponseEntity returns an empty JSON (I guess?) and the logger tells me RequestResponseBodyMethodProcessor : Nothing to write: null body.
Let's kick off with a snippet of my model. All 42 values are annotated with @JsonProperty, it contains an empty and full constructor, getters and setters and a to-String method.
public class TcoResult {
@JsonProperty("id")
private float id;
@JsonProperty("emissionCostsCO2Share")
private float emissionCostsCO2Share;
@JsonProperty("emissionCostsFineDustShare")
private float emissionCostsFineDustShare;
@JsonProperty("emissionCostsNoxShare")
private float emissionCostsNoxShare;
@JsonProperty("emissionCostsTotal")
private float emissionCostsTotal;
...
My Controller class in B:
@RestController
public class DataApiController implements ResultEventListener {
@Autowired
RestTemplate restTemplate;
@Autowired
DataApiService dataApiService;
@GetMapping(
value = "calculate/{id}",
produces = "application/json;charset=UTF-8"
)
public void getTcoInputFromDataApi(@PathVariable int id) {
dataApiService.setResultEventListenerController(this);
dataApiService.getTcoInputFromDataApi(id);
}
@PostMapping(
produces = "application/json;charset=UTF-8",
consumes = "application/json;charset=UTF-8"
)
public ResponseEntity<Object> postTcoResultToDataApi(@RequestBody TcoResult tcoResult) {
return dataApiService.postTcoResultToDataApi(tcoResult);
}
@Override
public void onEvent() {
this.postTcoResultToDataApi(dataApiService.getTcoResult());
}
}
Via Postman, I call the getTcoInputFromDataApi-method with a GET-Request. This returns the object with the given ID from my DataApi.
Let's getto the Service class in B:
@Service
public class DataApiService implements ResultEventListener{
@Autowired
RestTemplate restTemplate;
private Logger logger = LoggerFactory.getLogger(DataApiService.class);
private ResultEventListener resultEventListenerController;
private final String URI_TCORESULT = "resultTco";
private final String URI_TCOINPUT_ID = "inputTco/{id}";
private CalculationFactory calculationFactory;
private TcoInput tcoInput;
private TcoResult tcoResult;
// GET input for the tco from the DataApi by id
public void getTcoInputFromDataApi(int id) {
Map<String, String> params = new HashMap<>();
params.put("id", Integer.toString(id));
tcoInput = restTemplate.getForObject(URI_TCOINPUT_ID, TcoInput.class, params);
logger.info("Successfully read the tco input object from the DataApi");
this.onEvent();
}
// POST the results to the DataApi
public ResponseEntity<Object> postTcoResultToDataApi (TcoResult tcoResult123) {
logger.info(tcoResult123.toString());
// TcoResult toReturn = restTemplate.postForObject(URI_TCORESULT, tcoResult123, TcoResult.class);
//return new ResponseEntity<>(toReturn, HttpStatus.CREATED);
return new ResponseEntity<>(tcoResult123, HttpStatus.CREATED);
}
private void calculateResults(TcoInput tcoInput) {
calculationFactory = new CalculationFactory(tcoInput);
JSONDataOutput jsonDataOutput = calculationFactory.calculateAllCosts();
tcoResult = calculationFactory.getResultTco();
}
public void setResultEventListenerController(ResultEventListener resultEventListenerController) {
this.resultEventListenerController = resultEventListenerController;
}
public TcoResult getTcoResult() {
return tcoResult;
}
@Override
public void onEvent() {
logger.info("Calculating results...");
calculateResults(this.tcoInput);
logger.info("Calculations are done!");
if (tcoResult != null) {
logger.info("Calling listener of controller");
resultEventListenerController.onEvent();
}
}
}
As you can see, I work with custom listeners, so that logic gets triggered as soon as the calculation is done. All that works as intended.
The problem arises, when the logic reraches the postTcoResultToDataApi-method. For presentation's sake I just returned a new ResponseEntity with the TcoResult as paramter.
The commented code is the one I want to execute, but if I do so, my DataApi responds with a 422 code, which I implemented in my validation logic (because the given object somehow is null).
The controller of the DataApi are pretty basic and just save an object (when POST) or return a ResponseEntity with an object (when GET).
Here you can see the code of B exemplarily (as I said, nothing special).
@RestController
public class ResultTcoSimulationController {
private ResultTcoSimulationService resultTcoSimulationService;
@Autowired
public ResultTcoSimulationController(ResultTcoSimulationService resultTcoSimulationService) {
this.resultTcoSimulationService = resultTcoSimulationService;
}
@PostMapping(
value = "/resultTco",
consumes = "application/json;charset=UTF-8",
produces = "application/json;charset=UTF-8"
)
public ResponseEntity<Object> addResultForTcoSimulation(@RequestBody ResultTco resultTco) {
return resultTcoSimulationService.addResultForTcoSimulation(resultTco);
}
@GetMapping(
value = "/resultTco/{id}",
produces = "application/json;charset=UTF-8"
)
public ResponseEntity<Object> readResultForTcoSimulation(@PathVariable int id) {
return resultTcoSimulationService.readResultForTcoSimulation(id);
}
...
and the service class:
@Component
public class ResultTcoSimulationService {
@Autowired
@Lazy
private ResultTcoSimulationRepository resultTcoSimulationRepository;
Logger logger = LoggerFactory.getLogger(ResultTcoSimulationService.class);
// CREATE result for the TCO simulation
public ResponseEntity<Object> addResultForTcoSimulation(ResultTco resultTco) {
String exceptionMessage;
try {
validateObjectForPost(resultTco);
resultTcoSimulationRepository.save(resultTco);
logger.info("Successfully created the result for the TCO simulation with the id = " + resultTco.getId());
return new ResponseEntity<>(resultTco, HttpStatus.CREATED);
} catch(IllegalIdException e) {
exceptionMessage = e.getMessage();
return new ResponseEntity<>(exceptionMessage, HttpStatus.CONFLICT);
} catch(IllegalPassingParameterException e) {
exceptionMessage = e.getMessage();
return new ResponseEntity<>(exceptionMessage, HttpStatus.UNPROCESSABLE_ENTITY);
}
}
...
In the end, I just don't get why - in B - when wanting to post the object to A, the very instanciated object is null, when given as a parameter of the ResponseEntity, but has perfectly fine values, when printed in the logger.
I don't know if this is of any interest, but if I print that object in the logger with logger.info(tcoResult123.toString()); it calls the to-String method, if the line TcoResult toReturn = restTemplate.postForObject(URI_TCORESULT, tcoResult123, TcoResult.class); is executed, the terminal says Writing *basically the toString of the object* with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
