Here are some of approaches you can follow to handle your custom exceptions.
Create a POJO to handle your custom error messages and put your properties you want to return.
public class ErrorResponse {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Approach 1. Within your Controller method.
@RequestMapping("/car/{id}")
public ResponseEntity<?> getCar(@PathVariable String id) {
Car car = carService.getCar(id);
if (car == null) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("Record not found");
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND)
}
return new ResponseEntity<>(car, HttpStatus.OK);
}
Approach 2: Handle exceptions globally.
Step 1: Create NotFound exception class and extend to RunTime Exception.
public class NoRecordFoundException extends RuntimeException {
public NoRecordFoundException() {
super();
}
}
Step 2: Create Global Exception handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoRecordFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNoRecordFoundException(NoRecordFoundException ex)
{
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("No Record Found");
return errorResponse;
}
//same you can handle Exceptionerror for internal
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleDefaultException(Exception ex) {
ErrorResponse response = new ErrorResponse();
response.setMessage(ex.getMessage());
return response;
}
}
Step 3: throw Not found exception from your controller or service:
@RequestMapping("/car/{id}")
public ResponseEntity<?> getCar(@PathVariable String id) {
Car car = carService.getCar(id);
if (car == null) {
throw new NoRecordFoundException();
}
return new ResponseEntity<>(car, HttpStatus.OK);
}
Approach 3: Create @ExceptionHandler within controller and throw
@ExceptionHandler(NoRecordFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNoRecordFoundException(NoRecordFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("No Record Found");
return errorResponse;
}
Answer from kj007 on Stack OverflowHere are some of approaches you can follow to handle your custom exceptions.
Create a POJO to handle your custom error messages and put your properties you want to return.
public class ErrorResponse {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Approach 1. Within your Controller method.
@RequestMapping("/car/{id}")
public ResponseEntity<?> getCar(@PathVariable String id) {
Car car = carService.getCar(id);
if (car == null) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("Record not found");
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND)
}
return new ResponseEntity<>(car, HttpStatus.OK);
}
Approach 2: Handle exceptions globally.
Step 1: Create NotFound exception class and extend to RunTime Exception.
public class NoRecordFoundException extends RuntimeException {
public NoRecordFoundException() {
super();
}
}
Step 2: Create Global Exception handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoRecordFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNoRecordFoundException(NoRecordFoundException ex)
{
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("No Record Found");
return errorResponse;
}
//same you can handle Exceptionerror for internal
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleDefaultException(Exception ex) {
ErrorResponse response = new ErrorResponse();
response.setMessage(ex.getMessage());
return response;
}
}
Step 3: throw Not found exception from your controller or service:
@RequestMapping("/car/{id}")
public ResponseEntity<?> getCar(@PathVariable String id) {
Car car = carService.getCar(id);
if (car == null) {
throw new NoRecordFoundException();
}
return new ResponseEntity<>(car, HttpStatus.OK);
}
Approach 3: Create @ExceptionHandler within controller and throw
@ExceptionHandler(NoRecordFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleNoRecordFoundException(NoRecordFoundException ex) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("No Record Found");
return errorResponse;
}
The best way to handle any app specific exceptions is to create a custom exception class. Create a package saying com.randomorg.appname.exception. In that create an appexception class that extends the Java's Exception class.
public class CustomAppException extends Exception {
private String requestId;
// Custom error message
private String message;
// Custom error code representing an error in system
private String errorCode;
public CustomAppException (String message) {
super(message);
this.message = message;
}
public CustomAppException (String requestId, String message, String errorCode) {
super(message);
this.requestId = requestId;
this.message = message;
this.errorCode = errorCode;
}
public String getRequestId() {
return this.requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
@Override
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public String getErrorCode() {
return this.errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
}
Once this is done, ensure your controller uses this exception, this way you can customize any exception to become app based.
In your case, your methods like addCar, getCar, you can say it throws CustomAppException and then you can handle it in a simple try catch block.
To improvise further, you can further specialize exception by extending the CustomAppException class, say MyCustomException extends CustomAppException, this way you can organize your exception handling better. Let me know if you need more help on this. Happy to help.
Videos
It's because, following line does not return null, but an empty list.
Collection<Employee> result = this.employeeRepository.findByLastName(lastName);
You can either check for empty list as well for throwing Exception, or just return the empty list as well. I would not recommend throwing Exception for find/ search. I personally, throw any NotFoundException only for get methods like getById. Otherwise I just return the empty list.
if (result == null || result.isEmpty()) {
throw new EmployeeNotFoundException(lastName);
}
Also, modification to Hey-men-wattsup's code to send error code, instead of throwing exception in the Controller. This will send 404, NotFound code.
@ExceptionHandler(EmployeeNotFoundException.class)
public void handleException(EmployeeNotFoundException e, , HttpServletResponse response) {
response.sendError(HttpStatus.NOT_FOUND.value(), e.getMessage());
}
Spring Boot Test's MockMvc class combined with Mockito provides required methods to unit test a controller.
In case you're wondering about that EmployeeNotFoundException, it's thrown but not being handled.
You need at least one @ExceptionHandler annotated method to handle it.
@ExceptionHandler(EmployeeNotFoundException.class)
public void handleException(EmployeeNotFoundException e) {
// do something
}
Like so :
@RestController
@RequestMapping("/employee")
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@RequestMapping(value = "/findByLastName?{lastName}=", method = RequestMethod.GET)
Object getEmployees(@PathVariable String lastName) {
Collection<Employee> result = this.employeeRepository.findByLastName(lastName);
if (result == null) {
throw new EmployeeNotFoundException(lastName);
}
return result;
}
@ExceptionHandler(EmployeeNotFoundException.class)
public void handleException(EmployeeNotFoundException e) {
System.out.println("Exception triggered");
}
}
Definitely option 1, except that you don't have to handle it in the global exception handler if you annotate the exception class with ResponseStatus in the following way:
@ResponseStatus(code = HttpStatus.BAD_REQUEST)
class BadRequestException extends RuntimeException {}
I prefer the first solution, and eventually return with status code 400 to indicate input invalid.
That is why 400 status code is for. HTTP API Errors.
If most of the exceptions are handled like in code you have posted and you don't want to change return types of methods, just replace them with
catch(Exception e){
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
which will give the following output
{
"timestamp": "2021-03-10T09:25:04.823+0000",
"status": 404,
"error": "Not Found",
"message": "Not Found",
"path": "/account"
}
if you'd like to get exception info just add
catch(Exception e){
throw new ResponseStatusException(HttpStatus.NOT_FOUND,null, e);
}
response:
{
...
"message": "404 NOT_FOUND; nested exception is java.lang.ArithmeticException",
...
}
note that it will be logged if you didn't declare your own exception resolver
2021-03-10 15:28:20.043 WARN 11392 --- [nio-8090-exec-2] .w.s.m.a.ResponseStatusExceptionResolver : Resolved [org.springframework.web.server.ResponseStatusException: 404 NOT_FOUND "Not Found"; nested exception is java.lang.ArithmeticException]
Instead of null you could pass a string describing exception, which will replace "message" in json response.
One usually returns response entity with body object and status set to provide interesting information:
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, trackDTO.getId().toString()))
.body(result);
Response Entity is org.springframework.http.ResponseEntotuy which provides a lot of convenience methods to setup all the headers and whatever is necessary.