web-dev-qa-db-fra.com

Gestion des exceptions dans les contrôleurs Grails

Je sais comment faire la gestion des exceptions génériques dans Grails en utilisant UrlMappings et un ErrorController pour la gestion des exceptions génériques, de sorte que si une exception échappe à un contrôleur, l'utilisateur sera envoyé vers une page d'erreur générique et l'exception sera enregistrée. Je sais également comment utiliser les blocs try/catch pour gérer des exceptions spécifiques et tenter de les récupérer.

Mais dans la plupart des contrôleurs, je veux juste donner à l'utilisateur un message d'erreur légèrement plus spécifique si une exception se produit. Donc, dans l'action de création, je veux dire à l'utilisateur que l'élément n'a pas été créé. Ou dans l'action d'importation, je veux dire à l'utilisateur que l'importation a échoué. En ce moment, les contrôleurs ressemblent à:

class ThingController {
  def create = {
    try {
      // The real controller code, which quickly hands it off to a service
    } catch (Exception e) {
      handleException(e, "There was an error while attempting to create the Thing")
    }
  }

  def delete = {
    try {
      // The real controller code, which quickly hands it off to a service
    } catch (Exception e) {
      handleException(e, "There was an error while attempting to delete the Thing")
    }
  }

  private void handleException(Exception e, String message) {
    flash.message = message
    String eMessage = ExceptionUtils.getRootCauseMessage(e)
    log.error message(code: "sic.log.error.ExceptionOccurred", args: ["${eMessage}", "${e}"])
    redirect(action:index)
  }
}

Notez que les blocs catch ne font rien de différent selon le type ou le contenu de l'exception; ils donnent juste un message d'erreur légèrement plus descriptif basé sur le contrôleur. Le "vrai" code du contrôleur est généralement de 6 à 10 lignes, donc avoir 4 lignes de code supplémentaires juste pour changer le message d'erreur semble excessif. En outre, la règle CodeNarc "CatchException" se plaint, ce qui renforce mon opinion selon laquelle il doit y avoir une meilleure façon de le faire. Je suppose que d'autres applications Grails ont des exigences similaires. Quelle est la manière idiomatique de spécifier des messages d'erreur différents en fonction de l'action à partir de laquelle l'exception s'est propagée?

Je suis intéressé par les réponses qui proviennent de l'expérience avec une façon particulière de résoudre ce problème, ou mieux encore, un lien vers des bases de code où je peux voir la solution dans la pratique.

19
Tom Panning

Grails possède le mécanisme de gestion générale des exceptions de contrôleur. Vous pouvez le faire dans un contrôleur d'erreur dédié. Les contrôleurs réguliers n'ont pas besoin d'utiliser try/catch.

Manette:

class ThingController {
    def create() {
        def id = params.id as Long

        if (id == null) {
            throw new MissingPropertyException("thingId")
        }
        // The real controller code, which mostly parses things out and hands it
        // off to a service.
        // Service methods can throws exception

    }
}

Ajouter une erreur de gestion 500 dans UrlMappings:

class UrlMappings {

    static mappings = {
        // Exception handling in ErrorController
        "500"(controller: "error")
    }
}

ErrorController:

class ErrorController {

    def index() {

        def exception = request.exception.cause
        def message = ExceptionMapper.mapException(exception)
        def status = message.status

        response.status = status
            render(view: "/error", model: [status: status, exception: exception])
    }
}

Vous pouvez gérer REST et les exceptions non-REST en utilisant cette approche. Il existe également Declarative Exception Handling plugin mais je n'ai pas de

Mise à jour

Vous pouvez obtenir les messages d'erreur spécifiques dans le contrôleur d'erreur. Lorsque dans le contrôleur, lancez une nouvelle RuntimeException ("Il y a eu une erreur lors de la tentative de suppression de la chose"), puis dans le contrôleur error.exception.cause.message affichera un message: "Il y a eu une erreur lors de la tentative de suppression de la chose".

28
Andriy Budzinskyy

Voir aussi Comment savoir d'où a été lancée l'erreur 500 (Grails)

Je crée des pages d'erreur personnalisées basées sur des annotations sur les contrôleurs, donnant des procédures de gestion des exceptions communes à plusieurs contrôleurs.

class ErrorsController {
def index() {
    def initialController = request.exception?.className
    if (initialController) {
        def controller = grailsApplication.getArtefact("Controller", initialController).getReferenceInstance()
        // do some rendering based on the annotations
        render "Controller: ${initialController}, annotations ${controller.getClass().getDeclaredAnnotations()}"
        return
    }
    render 'no initial controller'
}
1
Igor