Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace exception handling structure

I trying to implement spring security + jwt.I done log-in and log-out methods, jwt filter,provider and web config is configured. So the main problem is my controller and how to return error messages to user, for example if user typed wrong password/username or user account is banned etc. I got a structure built on exception handling, looks terible.

controller

@PostMapping("/log-in")
    public ResponseEntity logIn(@RequestBody UserDto userDto) {
        log.info("[LOG-IN] user with username " + userDto.getUsername());
        try {
            HashMap<String, String> response = userService.logIn(userDto);
            return ResponseEntity.ok(response);

        } catch (UserStatusException ex) {
            return ResponseEntity.badRequest().body("Account is Pending");
        } catch (UsernameNotFoundException ex) {
            return ResponseEntity.badRequest().body("Could not find account!");
        } catch (AuthenticationException ex) {
            log.error("Wrong username or password!");
            return ResponseEntity.badRequest().body("Wrong username or password!");
        }
    }

service

@Override
    public HashMap<String, String> logIn(UserDto userDto)throws AuthenticationException, UserStatusException{
        User user = findByUsername(userDto.getUsername());

        authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword())); //login

        checkUserStatus(user);              //check if user pending or banned
        user.setUserStatus(UserStatus.ACTIVE);

        String token = jwtTokenProvider.createToken(user.getUsername(), user.getUserRoles());
        HashMap<String, String> response = new HashMap<>();
        response.put("token", token);
        response.put("username", user.getUsername());
        userRepository.save(user);
        return response;
    }
@Override
    public User findByUsername(String username)throws UsernameNotFoundException {
        log.info("[UserService, findByUsername]");
        User user = userRepository.findByUsername(username);
        if(user == null){
            log.error("User not found with {} username: ", username);
            throw new UsernameNotFoundException("User not found!");
        }
        log.info("User {} successfully loaded ",username);
        return user;
    }
@Override
    public void checkUserStatus(User user)throws UserStatusException {
        if (user.getUserStatus().equals(UserStatus.BANNED)
           || user.getUserStatus().equals(UserStatus.PENDING)) {
            throw new UserStatusException("Not confirmed");
        }
    }

Is there any other way to replace this structure?

like image 308
Aquaelia Avatar asked May 19 '26 17:05

Aquaelia


2 Answers

You should use a ControllerAdvice (see a tutorial here).

It's a special class that look like this

@ControllerAdvice
public class ControllerAdvice {

   @ExceptionHandler(PersonNotFoundException.class) 
   public ResponseEntity <VndErrors > notFoundException(final PersonNotFoundException e) {

    return error(e, HttpStatus.NOT_FOUND, e.getId().toString());

   }

}

It will allow you to bind a specific return code and response to each exception you need to handle, and it will automatically catch all exception returned by your controller. It's also a good way to handle all exceptions at the same place instead of over each exceptions...

I'm not sure about it, but I think you even can bind it to a specific mapping of your API for more granularity.

Hope this help! Have fun!

like image 199
Schisme Avatar answered May 22 '26 07:05

Schisme


You could add the repose status directly to your custom exception class:

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class UsernameNotFoundException extends RuntimeException {
    public UsernameNotFoundException(String message) {
        super(message);
    }

    public UsernameNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

In this way you don't need anymore to catch them in the controller and add the message and the status in ResponseEntity.

like image 44
Lungu Daniel Avatar answered May 22 '26 07:05

Lungu Daniel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!