Renaud is Not Bill Gates
Renaud is Not Bill Gates

Reputation: 2074

Exception Handling for REST services

I have a rest service for user registration, so when someone consume this service a random activation key is created then this activation key is sent to the user's email if there was no error during message sending this user will be stored into the database and a 200 HTTP status will be returned with user's informations.

if there was an error during message sending a 500 HTTP status will be retuned saying a costume message.

this is the code of my rest service :

@RequestMapping(value = "/candidats", method = RequestMethod.POST)
    public ResponseEntity<Candidat> saveCandidat(@RequestBody Candidat candidat){

        boolean mailException = false;

        String password=candidat.getPassword();
        String pass=candidat.getPassword();
        password=passwordencoder().encode(password);
        String KeyActivation=UUID.randomUUID().toString();
        candidat.setKeyActivation(KeyActivation);
        candidat.setPassword(password);
        candidat.setActivated(false);

        try{
            emailSender.send(candidat.getEmail(), "Email d'activation du compte", "merci pour votre inscription vous etes la bienvenue\n votre Username est :"+candidat.getUsername()+",et password :"+pass
                    +" merci de cliquer sur ce lien pour acitver votre compte "
                    + "http://localhost:8080/activateAccount?username="+candidat.getUsername()+"&activationKey="+candidat.getKeyActivation());
        }catch (MessagingException e){
            mailException = true;
        }
        if(mailException != false){
            Candidat cand=candidatMetier.saveCandidat(candidat);
            return new ResponseEntity<>(cand, HttpStatus.OK);
        }else{
            return new ResponseEntity<>("Email service is down", HttpStatus.INTERNAL_SERVER_ERROR);
        }
        
    }

the issue is that I don't know how to manage this since I'm returning two different types for ResponseEntity.

how can I solve this ?

Edit :

I used the spring @ExceptionHandler to handle the MessagingException as following :

In my service class :

public Candidat saveCandidat(Candidat candidat) throws MessagingException {

        String password=candidat.getPassword();
        String pass=candidat.getPassword();
        password =  new BCryptPasswordEncoder(12).encode(password);
        String KeyActivation= UUID.randomUUID().toString();
        candidat.setKeyActivation(KeyActivation);
        candidat.setPassword(password);
        candidat.setActivated(false);

        emailSender.send(candidat.getEmail(), "Email d'activation du compte", "merci pour votre inscription vous etes la bienvenue\n votre Username est :"+candidat.getUsername()+",et password :"+pass
                +" merci de cliquer sur ce lien pour acitver votre compte "
                + "http://localhost:8080/activateAccount?username="+candidat.getUsername()+"&activationKey="+candidat.getKeyActivation());

        return candidatRepository.save(candidat);
    }

In my Rest controller class :

@RequestMapping(value = "/candidats", method = RequestMethod.POST)
public ResponseEntity<Candidat> saveCandidat(@RequestBody Candidat candidat) throws MessagingException {

    Candidat cand=candidatMetier.saveCandidat(candidat);
    return new ResponseEntity<>(cand, HttpStatus.OK);

}

@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(MessagingException.class)
@ResponseBody
public String handleMessagingException(){
    return "Email service is down";
}

So what I'm trying to do here is to get an INTERNAL_SERVER_ERROR Http status with the message I wrote "Email service is down" and with the detailed error ex.getLocalizedMessage(); in my response body.

but instead I get this :

{
  "timestamp": 1460638057182,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "org.springframework.mail.MailAuthenticationException",
  "message": "Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted. Learn more at\n535 5.7.8  https://support.google.com/mail/answer/14257 7sm6423249wmn.7 - gsmtp\n",
  "path": "/candidats"
}

and I can't see where is the costume message I wrote.

Upvotes: 1

Views: 186

Answers (2)

user152468
user152468

Reputation: 3242

You can use the ExceptionHandler annotation provided by SpringMVC. Also you may want to use @ResponseBody instead of new ResponseEntity<...>().

@RestController
@RequestMapping(...)
public class CandidateController {

  @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler(MessagingException.class)
  @ResponseBody
  public String handleMessagingException(MessagingException e)  {
    return "error while sending email.";
  }

  @RequestMapping(value = "/candidats", method = RequestMethod.POST)
  public ResponseEntity<Candidat> saveCandidat(@RequestBody Candidat candidat) throws MessaginException{

    String password=candidat.getPassword();
    String pass=candidat.getPassword();
    password=passwordencoder().encode(password);
    String KeyActivation=UUID.randomUUID().toString();
    candidat.setKeyActivation(KeyActivation);
    candidat.setPassword(password);
    candidat.setActivated(false);

    emailSender.send(candidat.getEmail(), "Email d'activation du compte", "merci pour votre inscription vous etes la bienvenue\n votre Username est :"+candidat.getUsername()+",et password :"+pass
                +" merci de cliquer sur ce lien pour acitver votre compte "
                + "http://localhost:8080/activateAccount?username="+candidat.getUsername()+"&activationKey="+candidat.getKeyActivation());
    Candidat cand=candidatMetier.saveCandidat(candidat);
    return new ResponseEntity<>(cand, HttpStatus.OK);
  }
}

Not sure why this is not working for you. Here is a minimal example that works for me:

@RestController
@RequestMapping("/candidate")
public class CandidateController {

  @Autowired
  CandidatService candidatService;

  @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler(MessagingException.class)
  @ResponseBody
  public String handleMessagingException(MessagingException e){
    return "error while sending email.";
  }

  @RequestMapping(method = RequestMethod.POST)
  public ResponseEntity<Candidat> saveCandidat(@RequestBody Candidat candidat) throws MessagingException {
    return new ResponseEntity<>(this.candidatService.save(candidat), HttpStatus.OK);
  }
}

@Service
class CandidatService {

  public Candidat save(Candidat candidat) throws MessagingException {
    if(true) {
        throw new MessagingException("foo");
    }
    return candidat;
  }

}

Upvotes: 1

We are Borg
We are Borg

Reputation: 5313

Why not just send simply send back a String? Or if you want the object on the client-side, then you can add a variable inside it, and access it on client-side.

But, you have too much service-side logic in your controller, and you are sending it in ResponseEntity<Object>. This is unnecessary. You can do it like this :

    @RequestMapping(value = "/candidats", method = RequestMethod.POST)
        public @ReponseBody Candidat saveCandidat(@RequestBody Candidat candidat){

    Candidat savedObject = this.serviceLayer.saveCandidate(candidate);
return savedObject;
}

On Client side you can simply access String like this :

String responseBody = responseEntity.getBody();

And then you can take remaining action on client side.

Upvotes: 1

Related Questions