0

I am developing a sample spring-boot API that handles login, the API works fine however , I am having a problem during error handling. All the error handling I have seen in tutorials is done in the controller, however my logic is in the service and thats where I would like to do error handling.

How can I achieve error handling of different type of errors in the service or is it considered bad practice to do so. My current problem is that my error message looks like this :

{

 "message": "Please Activate account before attempting to Sign in",
  "token": " ",
  "accountLocked": false,
  "accountEnabled": false,
  "status": false,
  "balance": " ",
  "profileImage": null,
  "roles": null,
  "baraAuthorities": null,
  "customerNumber": 0,
  "userEmail": "",
  "name": ""
}

However I would like to have just the message and the status show and hide the rest in the response, how do I achieve this :

{
  "message": "Please Activate account before attempting to Sign in",
  "status": false
}

This is my code for LoginService :

@Service
public class LoginService implements UserDetailsService {

    @Autowired
    private UserLoginRepository loginRepository;
    @Autowired
    private LoginInformationRepository logininfoRepository;

    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    String balance, message, pin, token, userEmail, name, profileImage = "";
    Boolean status, successStatus, accounState = false;
    int customerNumber, attempts;
    List<Roles> userRoles = new ArrayList();

    private boolean userNameExist(String username) {
        UserLogin user = loginRepository.findByUsername(username);
        if (user != null) {
            return true;
        }
        return false;
    }

    public LoginResponse verifyLoginDetails(LoginObject user) throws LoginException {

        AuthoritiesKeys userAuth = new AuthoritiesKeys();
        Merchant searchedMerchant = merchantRepository.findByUserlogin_LoginCredentialsID(userdetails.getLoginCredentialsID());
        DateUtility currentDate = new DateUtility();
        Boolean status = false;

        if (userdetails == null) {
            return new LoginResponse("Unable to Login. Please check login details and try again",status);
        } else {

            pin = user.getPassword();
            attempts = logininfoRepository.countByTodayDateAndUserLoginLoginCredentialsIDAndLoginSuccessStatusFalse(currentDate.getCurrentDate(), userdetails.getLoginCredentialsID());
            if (attempts < 3) {
                if (bCryptPasswordEncoder.matches(pin, userdetails.getPassword())) {

                    if (userdetails.isEnabled() == true) {
                        if (searchedMerchant != null) {

                            message = "Logged in Successfully";
                            status = userdetails.isAccountNonLocked();
                            accounState = userdetails.isEnabled();
                            userRoles = (List<Roles>) userdetails.getAuthorities();
                            balance = searchedMerchant.getAccount().getBalance().toString();
                            successStatus = true;
                            customerNumber = searchedMerchant.getMerchantNumber();
                            userEmail = searchedMerchant.getEmail();
                            name = searchedMerchant.getBusinessName();

                            return new LoginResponse(message, token, accounState, status, userRoles, successStatus, balance, userAuth, profileImage, customerNumber, userEmail, name);

                        } else {
                            return new LoginResponse("Error Merchant Information Error . Please contact the team",status);
                        }

                    } else {
                        return new LoginResponse("Please Activate account before attempting to Sign in",status);
                    }
                } else {
                    return new LoginResponse("Wrong Username or Password",status);
                }

            } else {
                userdetails.setAccountNonLocked(false);
                userdetails.setEnabled(false);
                loginRepository.save(userdetails);
                return new LoginResponse("Too many attempts account has been locked",status);
            }


        }

    }

This is my code for LoginResponse :

 public class LoginResponse {

    private String message;
    private String token;
    private Boolean accountLocked;
    private Boolean accountEnabled;
    private Boolean status;
    private String balance;
    private String profileImage;
    List<Roles> roles = new ArrayList<>();
    AuthoritiesKeys baraAuthorities = new AuthoritiesKeys();
    private int customerNumber;
    private String userEmail;
    private String name;

    public LoginResponse() {
    }

    public LoginResponse(String message, Boolean status) {
        this.message = message;
        this.status = status;
    }

    public LoginResponse(String message, String token, Boolean accountLocked, Boolean accountEnabled, List<Roles> myroles, Boolean status, String balance, AuthoritiesKeys userBaraAuthorities, String profileImage, int customerNumber, String userEmail, String name) {
        this.message = message;
        this.token = token;
        this.accountLocked = accountLocked;
        this.accountEnabled = accountEnabled;
        this.status = status;
        this.balance = balance;
        this.roles = myroles;
        this.baraAuthorities = userBaraAuthorities;
        this.profileImage = profileImage;
        this.customerNumber = customerNumber;
        this.userEmail = userEmail;
        this.name = name;
    }

… // left getter and setters for brevity

}

This my code for LoginController :

@RestController
@Api(value = "Login API", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserLoginController {

    @Autowired
    private LoginService loginService;
    @RequestMapping(method = RequestMethod.POST, value = "/api/usermanagement/user/login")
    @ApiOperation("login registered user")
    @ApiResponses(value = {
        @ApiResponse(code = 200, message = "OK", response = ResponseMessage.class)})
    public LoginResponse Login(@Valid @RequestBody LoginObject credentails) throws LoginException {
        return loginService.verifyLoginDetails(credentails);

    }

}
2
  • error handling should be done at most levels, and furthermore if there is an error, then is it relevant to the user, and if so, how do you propose to let them know? Commented May 25, 2018 at 0:52
  • I updated the question Commented May 25, 2018 at 1:03

1 Answer 1

1

Use should use ResponseEntityExceptionHandler class to handle exception like below

Create a class GlobalControllerExceptionHandler

import javax.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation 
.ResponseEntityExceptionHandler;

@ControllerAdvice
public class GlobalControllerExceptionHandler extends 
ResponseEntityExceptionHandler {

private static final Logger LOGGER = 
LoggerFactory.getLogger(GlobalControllerExceptionHandler.class);

////////////  Globle exception handler  ///////////
@ExceptionHandler(Exception.class)
public ResponseEntity handleAnyException(final Exception e,final WebRequest 
request) {
    LOGGER.error("Error occurred while processing request: {}", 
e.getMessage());
    e.printStackTrace();
    ResponseDTO responseDTO = new ResponseDTO();
    responseDTO.setSuccess(false);
    responseDTO.setCode(500);
    responseDTO.setMessage(e.getMessage());
    return new ResponseEntity<>(responseDTO, 
HttpStatus.INTERNAL_SERVER_ERROR);
}

////////////Self define exception class handler  ///////////

@ExceptionHandler(InvalidCredentialException.class)
public ResponseEntity handleInvalidCredentialException(final 
InvalidCredentialException e,final WebRequest request) {
    LOGGER.error("Invalid Credential: "+ e.getCause());
    ResponseDTO responseDTO = new ResponseDTO();
    responseDTO.setSuccess(false);
    responseDTO.setCode(e.getErrorCode());
    responseDTO.setMessage(e.getMessage());
    return new ResponseEntity<>(responseDTO, HttpStatus.UNAUTHORIZED);
}}

Create class InvalidCredentialException

public class InvalidCredentialException extends RuntimeException {

private static final long serialVersionUID = -3338166707608147652L;
private int errorCode;

public InvalidCredentialException() {

}

public InvalidCredentialException(final Throwable cause) {
    super(cause);
}

public InvalidCredentialException(final String message) {
    super(message);
}

public InvalidCredentialException(final int errorCode, final String message) {
    super(message);
    this.setErrorCode(errorCode);
}

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

public int getErrorCode() {
    return errorCode;
}}

Now throw an error like below

throw new InvalidCredentialException(401, "No project associated with this id");

I will suggest you that don't use try-catch block in your code(unless you need) because if any exception occur and not handled by catch block then it will go in global exception class Exception handler. Here ResponseDTO is also a self define class which has some variable like errorCode,isSuccess,errorMessage etc.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.