web-dev-qa-db-fra.com

Comment réparer les "'jets' d'exception pris localement"

Dans cette fonction qui gère un appel d'API REST, l'une des fonctions appelées pour traiter des parties de la demande peut générer une erreur indiquant qu'un code d'erreur doit être envoyé en tant que réponse. Cependant, la fonction elle-même peut également détecter une erreur, à quel point elle doit sauter dans le bloc de gestion des exceptions.

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            throw new ForbiddenException("You're not allowed to do that.");
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Webstorm soulignera la throw avec le message suivant: 'throw' of exception caught locally. This inspection reports any instances of JavaScript throw statements whose exceptions are always caught by containing try statements. Using throw statements as a "goto" to change the local flow of control is likely to be confusing.

Cependant, je ne suis pas sûr de savoir comment remanier le code pour améliorer la situation.

Je pourrais copier le code du bloc catch dans le contrôle if, mais je pense que cela rendrait mon code moins lisible et plus difficile à gérer.

Je pourrais écrire une nouvelle fonction qui effectue la vérification isAllowed et lève une exception si elle ne réussit pas, mais cela semble éluder le problème plutôt que de résoudre un problème de conception que Webstorm aurait soi-disant signalé.

Utilisons-nous les exceptions de manière inappropriée, et c’est la raison pour laquelle nous rencontrons ce problème, ou l’erreur Webstorm est-elle simplement erronée et doit-elle être désactivée?

16
cib

Vous recherchez quelque chose et lancez une exception si isAllowed échoue, mais vous savez quoi faire dans cette situation - appelez sendErrorCode. Si vous ne savez pas comment gérer la situation, c’est-à-dire dans des circonstances exceptionnelles, vous devriez lancer des exceptions aux appelants externes.

Dans ce cas, vous avez déjà défini le processus à suivre si cela se produit - utilisez-le directement sans le lancer/attraper indirect:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode("You're not allowed to do that.");
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

Je pourrais copier le code du bloc catch dans la vérification if, mais je pense que cela rendrait mon code moins lisible et plus difficile à gérer.

Au contraire, comme ci-dessus, je m'attendrais à ce que ce soit le moyen de gérer cette situation.

20
James Thorpe

Comme il ne s’agit pas d’une erreur de blocage, mais seulement d’une recommandation IDE, la question doit être examinée sous deux angles.

Le premier côté est la performance. S'il s'agit d'un goulot d'étranglement et qu'il est potentiellement possible de l'utiliser avec la compilation ou lors du transfert vers de nouvelles versions (non encore publiées) de nodejs, la présence de répétitions n'est pas toujours une mauvaise solution. Il semble que IDE indique précisément dans ce cas et qu'une telle conception peut conduire à une mauvaise optimisation dans certains cas.

Le deuxième côté est la conception du code. Si cela rend le code plus lisible et simplifie le travail des autres développeurs, conservez-le. De ce point de vue, des solutions ont déjà été proposées ci-dessus.

1
SlowSuperman

Il existe de bonnes réponses à la question "Pourquoi ne pas utiliser les exceptions comme contrôle de flux normal?" ici .

La raison pour laquelle vous ne capturez pas d'exception localement, c'est que vous savez localement comment gérer cette situation. Elle n'est donc, par définition, pas exceptionnelle.

La réponse de _James Thorpe me convient bien, mais @matchish estime qu'elle viole DRY. Je dis cela en général, ce n'est pas le cas. DRY, qui signifie "ne vous répétez pas", est défini par les personnes qui ont inventé l'expression comme suit: "Chaque élément de connaissance doit avoir une représentation unique, non ambiguë et faisant autorité dans un système". Comme appliqué à l'écriture de code logiciel, il s'agit de ne pas répéter le code complexe

Pratiquement tout code réputé violer DRY est dit "corrigé" en extrayant le code répété dans une fonction, puis en appelant cette fonction à partir des emplacements où elle a été répétée précédemment. Avoir plusieurs parties de votre appel de code sendErrorCode est la solution permettant de résoudre un problème DRY. Toute la connaissance de ce qu'il faut faire avec l'erreur se trouve dans un endroit définitif, à savoir la fonction sendErrorCode

Je modifierais légèrement la réponse de @James Thorpe, mais il s’agit plus d’un argument que d’une critique réelle, selon laquelle sendErrorCode devrait recevoir des objets ou des chaînes d’exception, mais pas les deux:

static async handleRequest(req) {
    try {
        let isAllowed = await checkIfIsAllowed(req);
        if (!isAllowed) {
            sendErrorCode(new ForbiddenException("You're not allowed to do that."));
            return;
        }
        let result = await doSomething(req); // can also raise exceptions
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}

La grande question est de savoir quelle est la probabilité de l'erreur et s'il est approprié de traiter !isAllowed comme une exception. Les exceptions sont conçues pour gérer des situations inhabituelles ou imprévisibles. Je m'attendrais à ce que !isAllowed soit une occurrence normale qui devrait être gérée avec une logique spécifique à cette situation, contrairement à une impossibilité soudaine d'interroger la base de données contenant la réponse à la question isAllowed.

La solution proposée par @ matchish's solution proposée modifie le contrat doSomethingOnAllowedRequest de manière à ne jamais lancer d'exception à quelque chose qui générera systématiquement une exception, plaçant la charge de la gestion des exceptions sur tous ses appelants. Cela est susceptible de provoquer une violation de DRY en obligeant plusieurs appelants à répéter le même code de traitement des erreurs. Par conséquent, je ne l'aime pas dans l'abstrait. En pratique, cela dépend de la situation générale, par exemple du nombre d’appelants présents et partage-t-il la même réponse aux erreurs. 

0
Old Pro

La réponse de James Thorpe a un inconvénient à mon avis. Ce n'est pas DRY, dans les deux cas, lorsque vous appelez sendError, vous gérez des exceptions. Imaginons que nous ayons beaucoup de lignes de code avec une logique comme celle-ci où Exception peut être lancé. Je pense que ça peut être mieux.

C'est ma solution

async function doSomethingOnAllowedRequest(req) {
    let isAllowed = await checkIfIsAllowed(req);
    if (!isAllowed) {
       throw new ForbiddenException("You're not allowed to do that.");
    }
    doSomething(req);
}
static async handleRequest(req) {
    try {
        let result = await doSomethingOnAllowedRequest(req);
        sendResult(result);
    } catch(err) {
        sendErrorCode(err);
    }
}
0
matchish