Rozpakowywanie za pomocą `errors.Is`
Część sekcji Logika i przepływ sterowania ścieżki GO w Coddy. Lekcja 40 z 68.
Podczas pracy z opakowanymi błędami potrzebujesz sposobu na sprawdzenie, czy określony błąd występuje gdziekolwiek w łańcuchu błędów. Funkcja errors.Is zapewnia taką możliwość, pozwalając identyfikować błędy wartownicze, nawet jeśli zostały opakowane wielokrotnie.
Funkcja errors.Is przyjmuje dwa parametry: błąd, który chcesz sprawdzić, oraz błąd docelowy, którego szukasz. Zwraca true, jeśli błąd docelowy zostanie znaleziony w dowolnym miejscu łańcucha błędów:
var ErrNotFound = errors.New("not found")
wrappedErr := fmt.Errorf("failed to load user: %w", ErrNotFound)
if errors.Is(wrappedErr, ErrNotFound) {
fmt.Println("This is a not found error")
}To nowoczesny, zalecany sposób sprawdzania konkretnych błędów w Go. W przeciwieństwie do porównywania ciągów znaków lub bezpośredniego sprawdzania równości, errors.Is działa poprawnie w przypadku opakowanych błędów i podąża za łańcuchem błędów, aby znaleźć dopasowania. Dzięki temu obsługa błędów jest solidna i niezawodna, nawet gdy błędy przechodzą przez wiele warstw aplikacji.
Wyzwanie
ŁatwyZbuduj system monitorowania połączeń z bazą danych, który używa errors.Is do identyfikowania określonych błędów połączenia w łańcuchach opakowanych błędów. To wyzwanie pokazuje, jak używać errors.Is do wykrywania błędów wartowniczych, nawet gdy zostały opakowane dodatkowym kontekstem na wielu warstwach aplikacji.
Otrzymasz dwa dane wejściowe:
- Ciąg zawierający szczegóły operacji na bazie danych w formacie
"database_name,operation,error_type"(np."users_db,connect,connection_timeout") - Ciąg zawierający kontekst aplikacji w formacie
"service_name,retry_count"(np."AuthService,3")
Twoim zadaniem jest:
- Zdefiniować trzy błędy wartownicze za pomocą
errors.New():ErrConnectionTimeoutz komunikatem"connection timeout"ErrConnectionRefusedz komunikatem"connection refused"ErrDatabaseNotFoundz komunikatem"database not found"
- Przeanalizować pierwsze dane wejściowe, dzieląc je po przecinkach, aby uzyskać nazwę bazy danych, operację i typ błędu
- Przeanalizować drugie dane wejściowe, dzieląc je po przecinkach, aby uzyskać nazwę usługi i liczbę ponownych prób
- Utworzyć pierwotny błąd na podstawie typu błędu:
- Jeśli typ błędu to
"connection_timeout": użyćErrConnectionTimeout - Jeśli typ błędu to
"connection_refused": użyćErrConnectionRefused - Jeśli typ błędu to
"database_not_found": użyćErrDatabaseNotFound - W przypadku każdego innego typu błędu: użyć
errors.New("unknown database error")
- Jeśli typ błędu to
- Utworzyć opakowany błąd na poziomie bazy danych za pomocą
fmt.Errorfi%w:"failed to [operation] database [database_name]: %w" - Utworzyć opakowany błąd na poziomie usługi:
"[service_name] database operation failed after [retry_count] retries: %w" - Użyć
errors.Is, aby sprawdzić, czy końcowy opakowany błąd odpowiada każdemu z błędów wartowniczych, i wyświetlić wyniki:"Checking for connection timeout: [true/false]""Checking for connection refused: [true/false]""Checking for database not found: [true/false]"
- Wyświetlić łańcuch błędów:
"Error Chain:""Original: [original_error_message]""Database level: [database_level_error_message]""Service level: [service_level_error_message]"
- Wyświetlić analizę błędu:
"Error Analysis:""Database: [database_name]""Operation: [operation]""Service: [service_name]""Retries: [retry_count]""Error type detected: [detected_error_type]", gdzie detected_error_type to jedna z wartości "Connection Timeout", "Connection Refused", "Database Not Found" lub "Unknown Error"
Użyj pakietu strings, aby dzielić ciągi wejściowe po przecinkach, pakietu errors do obsługi błędów wartowniczych i errors.Is, a także pakietu fmt do opakowywania błędów. To wyzwanie pokazuje, jak errors.Is umożliwia niezawodne identyfikowanie błędów, które działa poprawnie również w przypadku opakowanych błędów, dzięki czemu jest niezbędne do niezawodnej obsługi błędów w złożonych aplikacjach.
Spróbuj swoich sił
package main
import (
"errors"
"fmt"
"strings"
)
func main() {
// Wczytaj dane wejściowe
var dbOperation string
var appContext string
fmt.Scanln(&dbOperation)
fmt.Scanln(&appContext)
// Przetwórz dane wejściowe
dbParts := strings.Split(dbOperation, ",")
databaseName := dbParts[0]
operation := dbParts[1]
errorType := dbParts[2]
contextParts := strings.Split(appContext, ",")
serviceName := contextParts[0]
retryCount := contextParts[1]
// TODO: Napisz swój kod poniżej
// 1. Zdefiniuj trzy błędy wartownicze za pomocą errors.New()
// 2. Utwórz pierwotny błąd na podstawie typu błędu
// 3. Utwórz opakowany błąd na poziomie bazy danych za pomocą fmt.Errorf i %w
// 4. Utwórz opakowany błąd na poziomie usługi
// 5. Użyj errors.Is, aby sprawdzić każdy z błędów wartowniczych
// 6. Wyświetl wyniki i łańcuch błędów
}Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Logika i przepływ sterowania
1Zaawansowane sterowanie przepływem
Instrukcja switch z `fallthrough`Przerywanie zagnieżdżonych pętliKontynuowanie określonej pętliInstrukcja `goto`Podsumowanie — zaawansowane sterowanie pętlami4Projekt: Prosta lista zadań
Konfiguracja projektuDodawanie zadania7Obsługa błędów — dogłębnie
Własne typy błędówOpakowywanie błędów za pomocą `%w`Rozpakowywanie za pomocą `errors.Is`Rozpakowywanie za pomocą `errors.As`Zrozumienie `panic`Używanie `recover`Podsumowanie — bezpieczne dzielenie2Struktury i metody
Definiowanie metod dla strukturOdbiorniki przez wartośćOdbiorniki przez wskaźnikWybór odbiornikówMetody a funkcjePodsumowanie — działanie struktur5Mapy w szczegółach
Mapy strukturWskaźniki jako wartości mapSprawdzanie, czy mapy są nilPorównywanie mapPodsumowanie — licznik częstości słów3Interfejsy (podstawy)
Czym jest interfejs?Definiowanie interfejsuImplementowanie interfejsuUżywanie typów interfejsowychPusty interfejsAsercje typuPrzełącznik typówPodsumowanie – kształty i zachowaniaPoćwicz samodzielnie: Kompilator Go online