I think best practice to handle exception is to use @ControllerAdvice i.e global exception handler.
Don't throw an exception from controller layer, throw exception from service layer and internally will call Global exception handler i.e ControllerAdvice class to execute corresponding exception method and then send proper error message to the response.
**ControllerAdvice class**
```
@ControllerAdvice
public class ExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> notFound(UserNotFoundException userNotFoundException) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND, userNotFoundException.getMessage());
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
}
```
**Controller code,**
```
@GetMapping
public ResponseEntity<UserDto> getUser(@RequestParam("id") String id) {
UserDto userDto = userService.getUserById(id);
return new ResponseEntity<UserDto>(userDto, HttpStatus.OK);
}
```
**Service layer code,**
```
@Override
public User getUserById(String id) throws NotFoundException {
Optional<User> user = userRepository.findById(Integer.parseInt(id));
if (!user.isPresent()) throw new UserNotFoundException("Record Not Found!");
return user.get();
}
```
Don't throw an exception at Controller layer instead throw an exception at service layer. Implement the Controller advice so whenever exception throws from the service layer so it will execute controller advice code and send response to the API.
Need to construct proper error message with error code in the Controller Advice, it should be readable and understandable to the user.
```
@ControllerAdvice
public class ExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> notFound(NotFoundException notFoundException) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND, notFoundException.getMessage());
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
}
```
Controller code,
```
@PutMapping
public ResponseEntity<UserDto> update(@RequestBody UserDto user) {
UserDto userDto = userService.updatedUser(user);
return new ResponseEntity<UserDto>(userDto, HttpStatus.OK);
}
```
Service layer code,
```
@Override
public User update(User newUser) throws NotFoundException {
Optional<User> user = userRepository.findById(newUser.getId());
if (!user.isPresent()) throw new NotFoundException("Record Not Found!");
return userRepository.save(newUser);
}
```