Wie behebt man “‘Throw’ of Exception gefangen lokal”?

Lesezeit: 12 Minuten

Benutzer-Avatar
Zib

In dieser Funktion, die einen REST-API-Aufruf verarbeitet, kann jede der aufgerufenen Funktionen zur Verarbeitung von Teilen der Anforderung einen Fehler ausgeben, um zu signalisieren, dass ein Fehlercode als Antwort gesendet werden soll. Die Funktion selbst kann jedoch auch einen Fehler entdecken, an welcher Stelle sie in den Ausnahmebehandlungsblock springen sollte.

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 wird das unterstreichen throw mit folgender Meldung: '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.

Ich bin mir jedoch nicht sicher, wie ich den Code umgestalten soll, um die Situation zu verbessern.

Ich könnte den Code aus dem kopieren und einfügen catch Block in die if check, aber ich glaube, das würde meinen Code weniger lesbar und schwieriger zu warten machen.

Ich könnte eine neue Funktion schreiben, die das tut isAllowed check und löst eine Ausnahme aus, wenn dies nicht gelingt, aber das scheint das Problem zu umgehen, anstatt ein Designproblem zu beheben, das Webstorm angeblich meldet.

Verwenden wir Ausnahmen auf eine schlechte Weise und stoßen wir deshalb auf dieses Problem, oder ist der Webstorm-Fehler einfach irreführend und sollte deaktiviert werden?

  • @matchish Hey – habe gerade dieses Kopfgeld bemerkt. Ich bin mir nicht sicher, welcher Teil meiner Antwort Ihrer Meinung nach gegen die DRY-Prinzipien verstößt? Das einzige, was ich denken kann, ist die sendErrorCode – aber es wird nicht wörtlich wiederholt; An einer Stelle sendet es einen sehr spezifischen Fehler aus diesem Codeblock, an der allgemeineren catch Es sendet einen allgemeineren Fehler, für den nicht codiert wurde …?

    – James Thorpe

    17. Januar 2019 um 15:23 Uhr

Benutzer-Avatar
J. Munson

Entgegen der Meinung von James Thorpe bevorzuge ich etwas das Wurfmuster. Ich sehe keinen zwingenden Grund, lokale Fehler im try-Block anders zu behandeln als Fehler, die tiefer im Call-Stack auftauchen … werfen Sie sie einfach. Meiner Meinung nach ist dies eine bessere Anwendung der Konsistenz.

Da dieses Muster konsistenter ist, eignet es sich natürlich besser zum Refactoring, wenn Sie die Logik im try-Block in eine andere Funktion extrahieren möchten, die sich möglicherweise in einem anderen Modul/einer anderen Datei befindet.

// main.js
try {
  if (!data) throw Error('missing data')
} catch (error) {
  handleError(error)
}

// Refactor...

// validate.js
function checkData(data) {
  if (!data) throw Error('missing data')
}

// main.js
try {
  checkData(data)
} catch (error) {
  handleError(error)
}

Wenn Sie den Fehler behandeln, anstatt den try-Block einzufügen, muss sich die Logik ändern, wenn Sie sie außerhalb des try-Blocks umgestalten.

Darüber hinaus hat die Behandlung des Fehlers den Nachteil, dass Sie daran denken müssen, frühzeitig zurückzukehren, damit der try-Block die Logik nicht weiter ausführt, nachdem der Fehler aufgetreten ist. Das kann man ganz leicht vergessen.

try {
  if (!data) {
    handleError(error)
    return // if you forget this, you might execute code you didn't mean to. this isn't a problem with throw.
  }
  // more logic down here
} catch (error) {
  handleError(error)
}

Wenn Sie sich Sorgen darüber machen, welche Methode leistungsfähiger ist, sollten Sie dies nicht tun. Die Behandlung des Fehlers ist technisch performanter, aber der Unterschied zwischen den beiden ist absolut trivial.

Ziehen Sie die Möglichkeit in Betracht, dass WebStorm hier etwas zu eigensinnig ist. ESLint hat nicht einmal eine Regel dafür. Jedes Muster ist vollständig gültig.

  • Ich stimme zu, obwohl ich glaube, dass es von der Situation abhängen würde. Aber für Situationen, in denen alle möglichen Fehler auf die gleiche Weise behandelt werden, denke ich, dass es den Code lesbarer macht.

    – Albert James Teddy

    30. März 2020 um 11:06 Uhr

  • das sollte die antwort sein

    – CommonSenseCode

    22. September 2020 um 17:45 Uhr

  • Einverstanden. Dies ist nur einer dieser lächerlichen Standards, die keinen einzigen Unterschied machen.

    – m4heshd

    21. Mai 2021 um 16:03 Uhr

  • Wenn Sie Ihre eigenen benutzerdefinierten Fehlerklassen haben, kann es manchmal gut funktionieren, verschiedene Arten von Fehlern zu werfen und sie dann alle an einem Ort abzufangen und sie je nach Typ unterschiedlich zu behandeln.

    – robby

    17. November 2021 um 22:46 Uhr

  • Gute Antwort hier, Sie müssen sich nicht nach hinten beugen, nur um einem Redakteur zu gefallen.

    – SamAko

    9. März um 16:37 Uhr

Benutzer-Avatar
James Thorpe

Sie suchen nach etwas und lösen eine Ausnahme aus, wenn isAllowed fehlschlägt, aber Sie wissen, was in dieser Situation zu tun ist – rufen Sie an sendErrorCode. Sie sollten Ausnahmen für externe Aufrufer werfen, wenn Sie nicht wissen, wie Sie mit der Situation umgehen sollen – dh in Ausnahmefällen.

In diesem Fall haben Sie bereits einen definierten Prozess, was zu tun ist, wenn dies passiert – verwenden Sie ihn einfach direkt ohne das indirekte Werfen/Fangen:

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);
    }
}

Ich könnte den Code aus dem kopieren und einfügen catch Block in die ifcheck, aber ich glaube, das würde meinen Code weniger lesbar und schwieriger zu warten machen.

Im Gegenteil, wie oben würde ich erwarten, dass dies der Weg ist, mit dieser Situation umzugehen.

  • “Sie sollten Ausnahmen für externe Anrufer werfen, wenn Sie nicht wissen, wie Sie mit der Situation umgehen sollen – dh in Ausnahmefällen.” Wenn das stimmt, erklärt das das Problem. Das bedeutet jedoch, dass wir Ausnahmen im gesamten Projekt falsch verwenden, nicht nur in dieser Funktion. Da es keine anderen Antworten gibt, werde ich diese akzeptieren.

    – cib

    2. November 2017 um 9:09 Uhr

  • James, ich stimme der Antwort zu, aber ein Freund hat mir das geschickt: dev.to/nedsoft/central-error-handling-in-express-3aej Was denkst du?

    – fsljfke

    26. Juli 2020 um 21:57 Uhr


  • Wenn Sie nur “sendErrorCode()” und dann zurückgeben müssen, ist dies in Ordnung. Wenn Sie jedoch mehrere Aktionen ausführen müssen, um einen Fehler zu beheben oder ihn zu bereinigen, dann vervielfältigen Sie Code. Noch schlimmer wird es, wenn Sie mehrere Stellen in Ihrem try-Block haben, an denen Sie einen internen Fehler erkennen können. Wenn Sie nach dem catch-Block unabhängig von Fehlern zusätzliche Arbeit leisten müssen, haben Sie keine Möglichkeit, dorthin zu gelangen, wenn Sie in den try-Block zurückkehren.

    – wojtow

    2. Januar 2021 um 5:41 Uhr

  • @wojtow Ihre Szenarien klingen so, als könnten sie durch Refactoring behandelt werden …

    – James Thorpe

    2. Januar 2021 um 7:45 Uhr

Benutzer-Avatar
LangsamSuperman

Da es sich hierbei nicht um einen blockierenden Fehler, sondern nur um eine IDE-Empfehlung handelt, sollte die Frage von zwei Seiten betrachtet werden.

Die erste Seite ist die Leistung. Wenn dies ein Engpass ist und möglicherweise beim Kompilieren oder beim Übertragen auf neue (noch nicht veröffentlichte) Versionen von nodejs verwendet werden kann, ist das Vorhandensein von Wiederholungen nicht immer eine schlechte Lösung. Es scheint, dass die IDE genau in diesem Fall Hinweise gibt und dass ein solches Design in einigen Fällen zu einer schlechten Optimierung führen kann.

Die zweite Seite ist das Code-Design. Wenn es den Code lesbarer macht und die Arbeit für andere Entwickler vereinfacht – behalte es. Unter diesem Gesichtspunkt wurden oben bereits Lösungen vorgeschlagen.

Geben Sie eine Versprechen-Ablehnung zurück, anstatt einen Fehler in den Try-Block zu werfen

  try {
    const isAllowed = await checkIfIsAllowed(request);

    if (!isAllowed) {
      return Promise.reject(Error("You're not allowed to do that."));
    }

    const result = await doSomething(request);

    sendResult(result);
  } catch (error) {
    throw error;
  }

Es gibt gute Antworten auf die Frage “Warum Ausnahmen nicht als normale Flusskontrolle verwenden?” hier.

Der Grund dafür, keine Ausnahme auszulösen, die Sie lokal abfangen, ist, dass Sie lokal wissen, wie man mit dieser Situation umgeht, also ist es per Definition keine Ausnahme.

Die Antwort von @James Thorpe sieht für mich gut aus, aber @matchish ist der Meinung, dass sie gegen DRY verstößt. Ich sage das im Allgemeinen nicht. DRY, was für „Don’t Repeat Yourself“ steht, wird von den Menschen definiert, die den Satz geprägt haben: „Jedes Wissen muss eine einzige, eindeutige, maßgebliche Repräsentation innerhalb eines Systems haben“. Bezogen auf das Schreiben von Softwarecode geht es darum, sich nicht zu wiederholen Komplex Code.

Praktisch jeder Code, der gegen DRY verstoßen soll, wird als “korrigiert” bezeichnet, indem der wiederholte Code in eine Funktion extrahiert und diese Funktion dann an den Stellen aufgerufen wird, an denen sie zuvor wiederholt wurde. Mehrere Teile Ihres Codeaufrufs haben sendErrorCode ist der Lösung um ein DRY-Problem zu beheben. Das gesamte Wissen darüber, was mit dem Fehler zu tun ist, befindet sich an einem endgültigen Ort, nämlich dem sendErrorCode Funktion.

Ich würde die Antwort von @ James Thorpe leicht ändern, aber es ist eher ein Streit als eine echte Kritik, das ist das sendErrorCode sollte Ausnahmeobjekte oder Zeichenfolgen empfangen, aber nicht beides:

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);
    }
}

Die größere Frage ist, wie wahrscheinlich der Fehler ist und ob er angemessen zu behandeln ist !isAllowed als eine Ausnahme. Ausnahmen sind für ungewöhnliche oder unvorhersehbare Situationen gedacht. Ich würde erwarten !isAllowed ein normaler Vorfall sein, der mit einer für diese Situation spezifischen Logik behandelt werden sollte, anders als beispielsweise eine plötzliche Unfähigkeit, die Datenbank abzufragen, die die Antwort auf die hat isAllowed Frage.

Die vorgeschlagene Lösung von @matchish ändert den Vertrag von doSomethingOnAllowedRequest von etwas, das niemals eine Ausnahme auslöst, zu etwas, das routinemäßig eine Ausnahme auslöst, wodurch die Last der Ausnahmebehandlung auf alle seine Aufrufer gelegt wird. Dies führt wahrscheinlich zu einer Verletzung von DRY, da mehrere Aufrufer denselben Fehlerbehandlungscode wiederholen, also mag ich es abstrakt nicht. In der Praxis würde es von der Gesamtsituation abhängen, wie viele Anrufer da sind und ob sie tatsächlich die gleiche Reaktion auf Fehler haben.

  • Was ist, wenn Sie Fehler protokollieren müssen? Sie sollten zwei Codezeilen ändern, deshalb denke ich, dass es nicht DRY ist.

    – passend

    21. Januar 2019 um 8:11 Uhr

  • Sie schreiben! isAllowed ist keine unvorhersehbare Situation, aber ich sehe ForbiddenException in Ihrem Code. handleRequest erstellt, um etwas Nützliches zu tun, und wenn wir es nicht tun können, denke ich, dass es normal ist, die Ausnahme auszulösen. Für mich sieht sendErrorCode so aus, als ob eine Ausnahme zum Frontend ausgelöst wird

    – passend

    21. Januar 2019 um 8:20 Uhr


  • @matchish “Was ist, wenn Sie Fehler protokollieren müssen?” Das !isAllowed ist in Bezug auf diesen Code nicht unbedingt ein Fehler/eine Ausnahme (es handelt sich um einen behandelten Anwendungsfall), daher ist es nicht erforderlich, ihn zu protokollieren. An diesem Punkt debattieren wir jedoch über Code, für den wir die zugrunde liegende Semantik und den Anwendungsfall solcher Dinge nicht kennen, und der weit vom ursprünglichen Zweck dieser Fragen und Antworten entfernt ist.

    – James Thorpe

    21. Januar 2019 um 9:07 Uhr

  • Ich spreche nicht von Protokollierung, es kann alles andere sein. Sie haben zwei Codeteile, die geändert werden müssen, wenn Sie nicht nur sendErrorCode benötigen. IMO ist es nicht TROCKEN

    – passend

    21. Januar 2019 um 9:38 Uhr

  • @matchish wie werden Fehler in einem eindeutigen Fehlerbehandlungscode protokolliert (sendErrorCode) eher ein Verstoß gegen SRP als das Protokollieren von Fehlern im Code, dessen Aufgabe es ist, Anfragen zu verarbeiten?

    – Alter Profi

    23. Januar 2019 um 7:14 Uhr

Benutzer-Avatar
passend

Die Antwort von James Thorpe hat meiner Meinung nach einen Nachteil. Es ist nicht DRY, in beiden Fällen behandeln Sie Ausnahmen, wenn Sie sendError aufrufen. Stellen wir uns vor, wir haben viele Codezeilen mit einer solchen Logik, in der Exception geworfen werden kann. Ich denke, es kann besser sein.

Das ist meine Lösung

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);
    }
}

  • Was ist, wenn Sie Fehler protokollieren müssen? Sie sollten zwei Codezeilen ändern, deshalb denke ich, dass es nicht DRY ist.

    – passend

    21. Januar 2019 um 8:11 Uhr

  • Sie schreiben! isAllowed ist keine unvorhersehbare Situation, aber ich sehe ForbiddenException in Ihrem Code. handleRequest erstellt, um etwas Nützliches zu tun, und wenn wir es nicht tun können, denke ich, dass es normal ist, die Ausnahme auszulösen. Für mich sieht sendErrorCode so aus, als ob eine Ausnahme zum Frontend ausgelöst wird

    – passend

    21. Januar 2019 um 8:20 Uhr


  • @matchisch “Was ist, wenn Sie Fehler protokollieren müssen?” Das !isAllowed ist in Bezug auf diesen Code nicht unbedingt ein Fehler/eine Ausnahme (es handelt sich um einen behandelten Anwendungsfall), daher ist es nicht erforderlich, ihn zu protokollieren. An diesem Punkt debattieren wir jedoch über Code, für den wir die zugrunde liegende Semantik und den Anwendungsfall solcher Dinge nicht kennen, und der weit vom ursprünglichen Zweck dieser Fragen und Antworten entfernt ist.

    – James Thorpe

    21. Januar 2019 um 9:07 Uhr

  • Ich spreche nicht von Protokollierung, es kann alles andere sein. Sie haben zwei Codeteile, die geändert werden müssen, wenn Sie nicht nur sendErrorCode benötigen. IMO ist es nicht TROCKEN

    – passend

    21. Januar 2019 um 9:38 Uhr

  • @matchish wie werden Fehler in einem eindeutigen Fehlerbehandlungscode protokolliert (sendErrorCode) eher ein Verstoß gegen SRP als das Protokollieren von Fehlern im Code, dessen Aufgabe es ist, Anfragen zu verarbeiten?

    – Alter Profi

    23. Januar 2019 um 7:14 Uhr

Benutzer-Avatar
Wladyslaw Didenko

Dies könnte Ihnen einige Tipps geben, vielleicht kann das die Ursache sein (nicht sicher, ob relevant). Die Catch-Anweisung fängt keinen ausgelösten Fehler ab

“Der Grund, warum Ihr Try-Catch-Block fehlschlägt, liegt darin, dass eine Ajax-Anforderung asynchron ist. Der Try-Catch-Block wird vor dem Ajax-Aufruf ausgeführt und die Anforderung selbst gesendet, aber der Fehler wird ausgelöst, wenn das Ergebnis zu einem späteren Zeitpunkt zurückgegeben wird ZEIT.

Wenn der Try-Catch-Block ausgeführt wird, gibt es keinen Fehler. Wenn der Fehler geworfen wird, gibt es keinen Try-Catch. Wenn Sie Try-Catch für Ajax-Anfragen benötigen, fügen Sie Ajax-Try-Catch-Blöcke immer innerhalb des Erfolgs-Callbacks ein, NIEMALS außerhalb.”

1107530cookie-checkWie behebt man “‘Throw’ of Exception gefangen lokal”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy