Reputation: 6605
I work on a Spring webStart application...
I have 2 (and potentially more) methods that process multi-tier Exception clause, as in:
...
try {
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
result = new ResponseEntity<>(partner.getId(), HttpStatus.OK);
} catch (EmployeePartnerNotFoundException ex) {
LOG.error(ex.getMessage() + " employee id: ", employeeId);
errorResponse = new ErrorResponse("500", ex.getMessage());
} catch (ReadOperationDeniedException ex) {
LOG.error("User doesn't have permissions to update employee's {} details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("403", "User doesn't have permissions to update employee's details");
} catch (Exception ex) {
LOG.error("something went wrong while updating employee's {} partner details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("500", "unspecified server error");
} finally {
result = (result != null) ? result : new ResponseEntity<>(errorResponse, HttpStatus.I_AM_A_TEAPOT); // should be INTERNAL_SERVER_ERROR
}
...
Another method is almost identical, apart for this change:
employeeService.updateEmployeePartner(employeeId, partner);
=>
employeeService.createEmployeePartner(employeeId, partner);
and catching EmployeePartnerAlreadyExistsException
in that block.
Now, to reduce code duplication, I want to group all Error handling code in one place (method), so I replaced the above code with the following
...
try {
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
result = new ResponseEntity<>(partner.getId(), HttpStatus.OK);
} catch (Exception ex) {
errorResponse = processException(ex, employeeId, "update");
} finally {
result = (result != null) ? result : new ResponseEntity<>(errorResponse, HttpStatus.I_AM_A_TEAPOT); // should be INTERNAL_SERVER_ERROR
}
...
private ErrorResponse processException(Exception ex, Long employeeId, String operation) {
ErrorResponse errorResponse;
if (ex.getClass().equals(EmployeePartnerNotFoundException.class) ||
ex.getClass().equals(EmployeePartnerExistsException.class)) {
LOG.error(ex.getMessage() + " employee id: ", employeeId);
errorResponse = new ErrorResponse("500", ex.getMessage());
} else if (ex.getClass().isInstance(ReadOperationDeniedException.class)) {
LOG.error("User doesn't have permissions to " + operation + " employee's {} details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("403", "User doesn't have permissions to " + operation + " employee's details");
} else { // Exception
LOG.error("something went wrong while trying to " + operation + " employee's {} partner details: {}", employeeId, ex.getMessage());
errorResponse = new ErrorResponse("500", "unspecified server error");
}
return errorResponse;
}
Is that a good enough approach or are there any patterns to handle exceptions in the above scenario by outsourcing the handling to a separate method/class?
Since it's a spring application, I also consider using Spring exception handlers, as in:
@ExceptionHandler(Exception.class)
, but that will only cover part of my requirements.
Upvotes: 1
Views: 1535
Reputation: 6605
That's what I ended up doing in the end, along with the reply by Sangam:
Individual Exception handlers worked quite well; Do note there is no need to put these in separate class.
But I still wonder if there is a similar pattern where the application is not Spring MVC?
public ResponseEntity<?> updatePartnerDetails(@PathVariable("employeeId") Long employeeId,
@RequestBody PersonDetails partnerDto) {
LOG.info("Updating partner details for employee {}, partner details {}", employeeId, partnerDto);
validateRequestValues(partnerDto);
// Try-catches were around this call
Person partner = PersonMapper.fromPersonDetails(partnerDto);
employeeService.updateEmployeePartner(employeeId, partner);
LOG.info("partner details updated for partner id {}", employeeId);
return new ResponseEntity<>(partner.getId(), HttpStatus.OK);
}
@ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: BAD_REQUEST
@ExceptionHandler({EmployeePartnerExistsException.class, EmployeePartnerNotFoundException.class})
public ResponseEntity<?> employeePartnerError(Exception ex) {
LOG.error(ex.getMessage());
return new ResponseEntity<Object>(new ErrorResponse(400, ex.getMessage()), HttpStatus.OK);
}
@ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: BAD_REQUEST
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<?> validationError(Exception ex) {
LOG.error(ex.getMessage());
return new ResponseEntity<Object>(new ErrorResponse(400, ex.getMessage()), HttpStatus.OK);
}
@ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: FORBIDDEN
@ExceptionHandler(ReadOperationDeniedException.class)
public ResponseEntity<?> forbidden(Exception ex) {
LOG.error("User doesn't have permissions to amend employee's details");
return new ResponseEntity<Object>(new ErrorResponse(403, "User doesn't have permissions to amend employee's details"), HttpStatus.OK);
}
@ResponseStatus(HttpStatus.I_AM_A_TEAPOT) // TODO: INTERNAL_SERVER_ERROR
@ExceptionHandler(Exception.class)
public ResponseEntity<?> unspecifiedError(Exception ex) {
LOG.error("User doesn't have permissions to amend employee's details");
return new ResponseEntity<Object>(new ErrorResponse(500, "Something went wrong while editing employee's details"), HttpStatus.OK);
}
Upvotes: 0
Reputation: 4506
Use @ControllerAdvice with your custom ErrorResponse and each Handler for seprate exceptions. Refer Custom error response Spring
Sample code:
@ControllerAdvice
public class GlobalExceptionHandlers {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandlers.class);
/***************** User Defined Exceptions *************************/
@ExceptionHandler({ EmployeePartnerNotFoundException.class })
public ResponseEntity<Object> handleEmployeePartnerNotFoundException(EmployeePartnerNotFoundException ex) {
logger.error("EmployeePartnerNotFoundException : ", ex);
ErrorResponse errorResponse = new ErrorResponse("500", ex.getMessage());
return new ResponseEntity<Object>(errorResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
// other exception handlers
}
Upvotes: 1