When the user creates a record with a name that's already exists in the DB , I'm returning a specific Exception.
@PostMapping("/campaigns")
public ResponseEntity<CampaignDTO> saveCampaign(@RequestBody CampaignDTO campaignDTO) throws ApiErrorResponse, Exception {
if (this.campaignService.getCampaignByName(campaignDTO.getName()) != null) {
throw new IllegalArgumentException("The value already exists!");
}
if (campaignDTO.getProducts() == null) {
ApiErrorResponse errorReponseDto = new ApiErrorResponse("No Products attached");
throw errorReponseDto;
}
campaignDTO = campaignService.saveCampaign(campaignDTO);
ResponseEntity<CampaignDTO> responseEntity = new ResponseEntity<>(campaignDTO , HttpStatus.CREATED);
return responseEntity; // return 201
}
And the Exception that I want to return to the Client is:
public class ApiErrorResponse extends Throwable {
private final String error;
//Any addtional info you might later want to add to it
public ApiErrorResponse(String error){
this.error = error;
}
public String getError(){
return this.error;
}
}
However , when I throw
IllegalArgumentException("The value already exists!")
It is caught by
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
How can we prevent this , and return "ApiErrorResponse" when the user inserts the same name ?
I want to return my Exception , not anything else.
CodePudding user response:
Here you have few decision. I will mark 2 of them. 1st is to return directly BadRequest for example with specific DTO - It is not best example with the Throwable, but you can create new ErrorResponseDTO:
@PostMapping("/campaigns")
public ResponseEntity<?> saveCampaign(@RequestBody CampaignDTO campaignDTO) throws ApiErrorResponse, Exception {
if (this.campaignService.getCampaignByName(campaignDTO.getName()) != null) {
ApiErrorResponse errorReponseDto = new ApiErrorResponse("The value already exists!");
return ResponseEntity.badRequest().body(errorReponseDto) // return 400
}
if (campaignDTO.getProducts() == null) {
ApiErrorResponse errorReponseDto = new ApiErrorResponse("No Products attached");
return new ResponseEntity<>(errorReponseDto , HttpStatus.BAD_REQUEST); // return 400
}
campaignDTO = campaignService.saveCampaign(campaignDTO);
ResponseEntity<CampaignDTO> responseEntity = new ResponseEntity<>(campaignDTO , HttpStatus.CREATED);
return responseEntity; // return 201
}
Other way is to use @ControllerAdvice
which will handle exception and will return what you want. This advice will be triggered after you throw the exception:
@ControllerAdvice
public class MyAdvice {
@ExceptionHandler(value = ApiErrorResponse.class)
public ResponseEntity<MyErrorResponse> handleException(ApiErrorResponse exception) {
return return ResponseEntity.badRequest().body(MyErrorResponse)
}
}
CodePudding user response:
a better way to define a global exception class and a global exception handler.
- the global exception class:
@EqualsAndHashCode(callSuper = true)
@Data
public class GlobalErrorInfoException extends RuntimeException {
private String message;
private HttpStatus status;
private Long timestamp;
public GlobalErrorInfoException(HttpStatus status, String message) {
super(message);
this.status = status;
this.message = message;
this.timestamp = System.currentTimeMillis();
}
}
- the global exception handler
@RestControllerAdvice
@Slf4j
public class GlobalErrorInfoHandler {
// other ExceptionHandler
// GlobalErrorInfoException handler
@ExceptionHandler(value = GlobalErrorInfoException.class)
public ResponseEntity<?> errorHandlerOverJson(GlobalErrorInfoException e) {
log.error("Global Exception ", e);
return new ResponseEntity<>(e.getMessage(), e.getStatus());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleRuntimeException(Exception e) {
log.error("error ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("internal server error");
}
}
- use
// your throw
throw new GlobalErrorInfoException(HttpStatus.BAD_REQUEST, "The value already exists!");
you can also handler IllegalArgumentException in the global exception handler.