Heim > Java > javaLernprogramm > Schlechte Praktiken bei der Fehlerbehandlung

Schlechte Praktiken bei der Fehlerbehandlung

WBOY
Freigeben: 2024-09-03 18:33:12
Original
644 Leute haben es durchsucht

Error Handling Bad Practices

Dieser Artikel ist eine Fortsetzung meines vorherigen Artikels zur Fehlerbehandlung in drei Schritten.

In diesem Artikel möchte ich einige schlechte Praktiken bei der Fehlerbehandlung und vielleicht ein paar schnelle Lösungen zeigen, falls Sie stolpern
über sie in Ihrer Codebasis.

Haftungsausschluss, dieser Blog-Beitrag ist nur eine persönliche Meinung und Sie müssen die Empfehlungen an Ihren spezifischen Anwendungsfall anpassen.

1. Ausnahmen schlucken

Ich stolpere regelmäßig über Codebasen, in denen Ausnahmen stillschweigend ignoriert werden. Vielleicht hatte der Entwickler es eilig oder wollte die Fehlerfälle später bearbeiten? Auf jeden Fall ist dies eine schlechte Vorgehensweise, denn wenn der Fehler auftritt:

  • Der Benutzer erhält keine Rückmeldung und denkt möglicherweise, dass der Vorgang erfolgreich war.
  • Es gibt keinen Stacktrace oder eine zu untersuchende Fehlermeldung.
  • Manchmal gibt es nicht einmal einen richtigen Protokolleintrag, sodass es fast unmöglich ist, den Fehler zu erkennen.

Schlechtes Beispiel:

  /**
   * Reads some data from another service or repo and returns it.
   * The repo might throw an exception, but we don't care.
   */
  public Mono<String> getHello() {
    try {
      return helloRepository.getHello()
          .map("Hello %s"::formatted);
    } catch (Exception e) {
      // do nothing
      return Mono.just("Hello World");
    }
  }
Nach dem Login kopieren

In diesem Beispiel fragen wir uns vielleicht, warum sich einige Benutzer darüber beschweren, dass sie nicht die richtige Hallo-Nachricht erhalten, aber wir sehen keine Fehler, wir denken vielleicht, dass die Benutzer nur verwirrt sind.

Option 1 – Behalten Sie den Try-Catch bei und behandeln Sie den Fehler

Behandeln Sie den Fehler dort, wo er auftritt, z. B. wenn möglich bereits im Repository.
Wenn Sie das Problem hier beheben müssen, protokollieren Sie zumindest den Fehler und geben Sie eine ordnungsgemäße Fehlermeldung zurück.

Minimales Refactoring

  • Protokollieren Sie den Fehler (optional auch den Stacktrace)
  • eine ordnungsgemäße Fehlermeldung zurückgeben
  public Mono<String> getHello() {
    try {
      return helloRepository.getHello()
          .map("Hello %s"::formatted);
    } catch (Exception e) {
      log.error("Error reading hello data from repository - {}", e.getMessage(), e);
      return Mono.error(new RuntimeException("Internal error reading hello data %s".formatted(e.getMessage()), e));
    }
  }
Nach dem Login kopieren

HINWEIS:

Dadurch könnte der Upstream-Code beschädigt werden, da Sie jetzt einen Fehler anstelle eines Standardwerts zurückgeben.

Option 2 – Richtiges reaktives Stream-Refactoring

Besser wäre es, den Fehler innerhalb der reaktiven Stream-API zu behandeln

  public Mono<String> getHelloHandling() {
    return helloRepository.getHello()
      .map("Hello %s"::formatted)
      // wrap the exception in a RuntimeException with a meaningful message
      .onErrorMap(e -> new RuntimeException("Internal error reading hello data from HelloRepository` %s".
          formatted(e.getMessage()), e));
  }
Nach dem Login kopieren

1b. Ausnahmen schlucken – mit einem reaktiven Stream

Das Muster aus Beispiel eins erscheint auch in reaktiven Streams:

  public Mono<String> getHello() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        // do nothing on error, just return a default value
        .onErrorReturn("Hello world");
  }
Nach dem Login kopieren

Das sieht sehr schön und sauber aus, oder? Aber wir werden nicht erkennen können, dass der Fehler im Repository ausgegeben wird!
Wenn es einen Standardwert gibt, sollte zumindest ein Fehlerprotokoll geschrieben werden.

Schadensbegrenzung

Wie im vorherigen Beispiel verpacken wir die Ausnahme in eine andere Ausnahme, dieses Mal in eine benutzerdefinierte Ausnahme, die es sogar einfacher macht, den spezifischen Ort zu erkennen, an dem diese Ausnahme ausgelöst wird

  public Mono<String> getHello2() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        .onErrorMap(
            e -> new CustomException("Error reading hello-data from repository - %s".formatted(e.getMessage()), e));
  }
Nach dem Login kopieren

2. Wiederholtes Protokollieren des Stacktrace für dieselbe Ausnahme

Das genaue Gegenteil des stillschweigenden Löschens von Ausnahmen besteht darin, dieselbe Ausnahme mehrmals zu protokollieren. Dies ist eine schlechte Vorgehensweise, da dadurch die Protokolle mit Hunderten oder Tausenden Zeilen von Stacktraces verwechselt werden, ohne dass eine zusätzliche Bedeutung entsteht.

In meinem schlimmsten Beispiel habe ich den gleichen Stacktrace fünfmal in den Protokollen gefunden, ohne überhaupt eine sinnvolle Nachricht.

Schlechtes Beispiel:

Der Controller:

@RestController
@AllArgsConstructor
@Slf4j
public class HelloController {

  private final HelloService helloService;

  @GetMapping("/hello")
  public Mono<ResponseEntity<String>> hello() {
    return helloService.getHello()
        .map(ResponseEntity::ok)
        .defaultIfEmpty(ResponseEntity.notFound().build())
        .onErrorResume(e -> {
          log.error("Error:", e);
          return Mono.error(e);
        });
  }
}
Nach dem Login kopieren

Und im Service:

@Service
@AllArgsConstructor
@Slf4j
public class HelloService {

  private final HelloRepository helloRepository;

  /**
   * Reads some data from another service or repo and returns it.
   */
  public Mono<String> getHello() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        .onErrorResume(e -> {
          log.error("Error:", e);
          return Mono.error(e);
        });
  }
}
Nach dem Login kopieren

... und wahrscheinlich noch an einigen weiteren Orten...

Schadensbegrenzung

Dies ist in meinem vorherigen Artikel 3-Schritte-Fehlerbehandlung erläutert, daher werde ich hier nicht noch einmal den Code zeigen, sondern eher eine Empfehlung:

  • Verfügen Sie über einen globalen Fehlerhandler, der den Fehler protokolliert und eine ordnungsgemäße Fehlermeldung an den Benutzer zurückgibt.
  • Vermeiden Sie im Code den Stacktrace, aber
    • Wenden Sie die Ausnahme in eine benutzerdefinierte Ausnahme oder Laufzeitausnahme mit einer aussagekräftigen Nachricht ein
    • Protokollieren Sie die Fehlermeldung (nicht den gesamten Stacktrace)
    • Protokollieren Sie den Stacktrace nur im globalen Fehlerhandler

3. Fangen Sie generische Ausnahmen ab

Das Abfangen allgemeiner Ausnahmen wie Exception oder Throwable kann zu unbeabsichtigtem Verhalten führen und das Debuggen erheblich erschweren. Es ist besser, bestimmte Ausnahmen abzufangen.

Schlechtes Beispiel

  public Mono<String> getHello() {
    try {
      return helloRepository.getHello();
    } catch (Exception e) {
      log.error("Error while fetching hello data", e);
      return Mono.empty();
    }
  }
Nach dem Login kopieren

Schadensbegrenzung

Bestimmte Ausnahmen abfangen, um verschiedene Fehlerszenarien angemessen zu behandeln.

  public Mono<String> getHello() {
    try {
      return helloRepository.getHello();
    } catch (SQLException e) {
      return Mono.error(new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e));
    } catch (IOException e) {
      // maybe perform a retry?
      return Mono.error(new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e));
    }
  }
Nach dem Login kopieren

und das Äquivalent unter Verwendung der reaktiven Stream-API

  public Mono<String> getHello() {
    return helloRepository.getHello()
        .onErrorMap(SQLException.class, e -> new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e))
        .onErrorMap(IOException.class, e -> new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e));
  }
Nach dem Login kopieren

4. (Übermäßige) Verwendung geprüfter Ausnahmen

Meiner Meinung nach sind überprüfte Ausnahmen ein Fehler in Java, sie sind nicht sehr nützlich und führen oft zu schlechten Praktiken und überladenem Code.

Schlechtes Beispiel

public void someMethod() throws IOException, SQLException {
  // some code that might throw an exception
}
Nach dem Login kopieren

Bei aktivierten Ausnahmen müssen Sie diese im aufrufenden Code behandeln, was die Verwendung anderer Muster, z. B. funktionale Programmierung oder reaktive Streams oder im Frühjahr der globale Fehlerhandler.

Ausnahme:
Geprüfte Ausnahmen sind nützlich, z.B. im Bibliothekscode, wo Sie den Benutzer zwingen möchten, die Ausnahme zu behandeln.

Schadensbegrenzung

Verwenden Sie ungeprüfte Ausnahmen für Szenarien, in denen vernünftigerweise nicht erwartet werden kann, dass sich der Anrufer erholt.

public void someMethod() {
  try {
    // some code that might throw an exception
  } catch (IOException | SQLException e) {
    throw new RuntimeException("An error occurred - %s".formatted(e.getMessage()), e);
  }
}
Nach dem Login kopieren

Using Exceptions for Control Flow

Using exceptions for control flow makes the code hard to understand and can lead to performance issues.

Bad Example

try {
  int value = Integer.parseInt("abc");
} catch (NumberFormatException e) {
  // handle the case where the string is not a number
}
Nach dem Login kopieren

Mitigation

Use a regular flow control mechanism like an if-statement.

String value = "abc";
if (value.matches("\\d+")) {
  int number = Integer.parseInt(value);
} else {
  // handle the case where the string is not a number
}
Nach dem Login kopieren

Conclusion

In this article I showed some bad practices in error handling and how to mitigate them.
I hope you found this article helpful and maybe you can use some of the recommendations in your codebase and in your next refactoring.

Das obige ist der detaillierte Inhalt vonSchlechte Praktiken bei der Fehlerbehandlung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage