web-dev-qa-db-fra.com

Contrôleur de service MVC Spring à ressort avec gestion des erreurs correctement?

Je me demandais comment implémenter correctement un contrôleur Spring censé servir de service REST. Surtout je veux essayer de rendre l'interface aussi RESTful que possible. De plus, j'aimerais utiliser les codes d'erreur HTTP afin que mes clients puissent agir en conséquence.

Je me demandais comment implémenter mes méthodes afin de renvoyer JSON si tout fonctionne bien (dans le corps de la réponse) ou d'envoyer un code d'erreur http ainsi qu'une raison personnalisée pour laquelle cela ne fonctionne pas la base de données). Cependant, je ne sais pas laquelle est la bonne? renvoyer une chaîne et ajouter les valeurs pour retourner à un modèle, ou renvoyer une table de hachage et y placer mes éléments? ou renvoyer les objets directement? mais alors que se passe-t-il si une erreur survient et que je ne peux pas retourner ladite classe? retourne null à la place? Je publie 2 ou 3 façons de le faire que je pourrais imaginer: 

@RequestMapping(value="/addUser", method= RequestMethod.POST)
public String addUser(@RequestBody User user, HttpServletResponse response, Model model) throws Exception{

    try{
        userService.addUser(user);
        model.addAttribute("user", userService.getUser(user.getUsername(), user.getPassword()));
        return "user";
    }catch(Exception e){
        model.addAttribute("error", e.toString());
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
        return "error";
    }
}

Ou plutôt de cette façon:

@RequestMapping(value="/addUser", method= RequestMethod.POST)
public @ResponseBody Map addUser(@RequestBody User user, HttpServletResponse response){
    Map map = new HashMap();
    try{
        userService.addUser(user);
        map.put("success", true);
        map.put("username", user.getUsername());
    }catch (KeyAlreadyExistsException e){
        map.put("success", false);
        map.put("Error", e.toString());
        response.sendError(HttpServletResponse.SC_FORBIDDEN, e.toString());
    }catch(Exception e){
        map.put("success", false);
        map.put("Error", e.toString());
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
    }
    finally {
        return map;
    }
}

Je réalise que le code n'est pas "juste comme il faut" mais je n'arrive pas à comprendre comment le rendre comme il se doit. Peut-être que certaines expériences pourraient aider? Merci pour le support déjà

24
pascalwhoop

Vous pouvez également intercepter vos exceptions avec @ExceptionHandler méthos annoté dans votre Rest Controller.

@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleException(Exception e) {
    return "return error object instead";
}

cela rendra votre contrôleur/contrôleur logique complet plus propre.

29
marco.eig

Premièrement, je pense que vous devriez toujours renvoyer un objet lors du renvoi de JSON. Même quand quelque chose se passe horriblement mal.

En cas de problème, définissez simplement response.setStatus() et renvoyez une ressource décrivant l'erreur.

public class ErrorResource implements Resource {
    private final int status;
    private final String message;

    public ErrorResource(int s, String m) {
        status = s;
        message = m;
    }

    public int getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

La ressource devient sérialisée et le résultat serait

{"status":500, "message":"Yay!"}

L'utilisation de Map fonctionnera, mais j'aimerais vous conseiller d'écrire des classes de ressources qui définissent l'objet à renvoyer. Ils seraient plus faciles à entretenir. Maps n'offre aucune structure alors que la structure est une partie très importante lors de la création d'un service REST 

Je ne pense pas que vous devriez renvoyer une ressource avec le message d'exception brut incorporé. Il peut potentiellement laisser échapper des informations que vous ne voulez pas que quiconque voie.

16
Bart

vous pouvez utiliser @ExceptionHandler avec @ControllerAdvice vérifier ceci link

8

utilisez la classe ResponseEntity pour exploiter l'erreur avec le code d'état http.

Vous pouvez essayer le code suivant:

@RequestMapping(value = "/profile", method = RequestMethod.GET) 
@ResponseBody @ResponseStatus(value = HttpStatus.OK) 

public ResponseEntity<UserVO> getUserProfile() 
{ 
   string userName = getUserAuthentication().getName(); 
   if (StringUtils.isEmpty(userName)) RestUtil.defaultJsonResponse(""); 
   User user = userService.getUserByUserNameWithCounters(userName); 
   return RestUtil.getJsonResponse(new UserVO(user)); 
}
0
agus w

Si vous souhaitez que l'ensemble de votre exception avec stackTrace soit transmise à votre client, @Bart a déclaré que vous devriez envoyer un objet "ErrorResource".

Une bibliothèque a dans le commerce:

<dependency>
  <groupId>com.github.zg2pro</groupId>
  <artifactId>spring-rest-basis</artifactId>
  <version>0.2</version>
</dependency>

ajoutez-le à votre projet puis ajoutez simplement une classe "@ControllerAdvice" à vos beans, comme expliqué dans le projet wiki .

Cela devrait gérer vos erreurs bien!

0
zg2pro