Implement Error Handling for APIs
You have designed API operations with more than just one resoponse to cover both the success and error case. In your implementation code you want to return a specific response body if an error happens.
Descriptionโ
This How-To will guide you through the steps to implement the handling of error cases in the API properly.
Preconditionโ
- You have modelled the API operations in the Solution Designer
- You have added multiple responses to your operations using different HTTP status codes
Use caseโ
In our use case example, you want to load a pet by a given id. If the pet is not present, an error response should be returned. Therefore, the API was modelled as follows:
- An API operation getPetById
- Multiple responses with the following HTTP status codes and response bodies:
- 200: Pet
- 404: ErrorResponse
Stepsโ
Implement the success case of your operationโ
/**
* Get a pet by given id. If pet is not found, an error response should be returned in the response body
*/
@Override
public ResponseEntity<Pet> getPetById(String petId) {
// load pet from repository
Optional<petstore.ppopetj.sdk.domain.pets.entity.Pet> petEntity = this.repo.getPets().getPet().findById(petId);
// return pet if it could have been found
if (petEntity.isPresent()) {
// create success response
return new ResponseEntity<>(MapperUtil.mapDomainPetToApiPet(petEntity.get()), HttpStatus.OK);
} else {
// Error case: Pet was not found
// TODO: handle it properly
}
}
Create a new Exceptionโ
Within a Java Domain Service project, error handling in the API is handled using Exceptions. It is recommended to create a new Exception class for that use case. Alternatively, you can also reuse existing Exception classes.
public class PetNotFoundException extends RuntimeException {
private String petId;
public PetNotFoundException(String petId) {
super();
this.petId = petId;
}
public String getPetId() {
return petId;
}
public void setPetId(String petId) {
this.petId = petId;
}
}
You can extend the Exception class to carry more details about the error.
Create an ExceptionHandlerโ
To handle the Exception and convert it into a valid HTTP response, an ExceptionHandler is needed.
Within the handler you can specify the Exception class, the HTTP status code and the response body that is returned as HTTP response.
@ControllerAdvice
public class PetApiExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(PetApiExceptionHandler.class);
@ExceptionHandler(PetNotFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public @ResponseBody ErrorResponse handleResourceNotFound(PetNotFoundException exception, HttpServletRequest request) {
log.error("Pet with id {} not found", exception.getPetId());
// create error response object
ErrorResponse errResponse = new ErrorResponse();
// set details of error response object
errResponse.setCode("E404");
errResponse.setMessage(String.format("Pet with id %s cannot be found", exception.getPetId()));
// return error response
return errResponse;
}
}
Extend API operation to throw errorโ
Inside the API operation you can now make use of the created exception in your error case:
@Override
public ResponseEntity<Pet> getPetById(String petId) {
//...
// return pet if it could have been found
if (petEntity.isPresent()) {
return new ResponseEntity<>(MapperUtil.mapDomainPetToApiPet(petEntity.get()), HttpStatus.OK);
} else {
// throw PetNotFoundException which is handled by the PetApiExceptionHandler
throw new PetNotFoundException(petId);
}
}
For Spring boot stack 2.0, if an error occurs during executing an agent, the system by default retries 5 times to successfully process execution. After 5 times of failure, the event will be published to a dead letter queue where it gets preserved for monitoring and further actions if needed.
It is quite common that multiple API operations share the same error handler. With this approach, you can harmonize your API behavior and make it more consistent.
You have successfully learned how to do proper error handling in a Java Domain Service project.