Rozpakowywanie za pomocą `errors.As`
Część sekcji Logika i przepływ sterowania ścieżki GO w Coddy. Lekcja 41 z 68.
O ile errors.Is pomaga identyfikować konkretne wartości błędów w łańcuchu, czasami trzeba uzyskać dostęp do rzeczywistych pól i danych niestandardowego typu błędu. Funkcja errors.As umożliwia to, znajdując pierwszy błąd w łańcuchu pasujący do określonego typu i wyodrębniając go do sprawdzenia.
Funkcja errors.As przyjmuje dwa parametry: błąd do zbadania oraz wskaźnik do zmiennej docelowego typu błędu. Jeśli znajdzie pasujący typ błędu w łańcuchu, przypisuje ten błąd do zmiennej i zwraca true:
var validationErr ValidationError
if errors.As(wrappedErr, &validationErr) {
fmt.Printf("Field: %s, Message: %s\n", validationErr.Field, validationErr.Message)
}Jest to szczególnie przydatne podczas pracy z niestandardowymi typami błędów, które zawierają dodatkowy kontekst. W przeciwieństwie do errors.Is, który sprawdza jedynie równość, errors.As wyodrębnia rzeczywistą instancję błędu, umożliwiając dostęp do jej pól i metod. Dzięki temu obsługa błędów w złożonych aplikacjach jest bardziej zaawansowana i dostarcza więcej informacji.
Wyzwanie
ŁatwyZbuduj system obsługi błędów API, który używa errors.As do wyodrębniania i analizowania niestandardowych typów błędów z łańcuchów opakowanych błędów. To wyzwanie pokazuje, jak używać errors.As, aby uzyskać dostęp do pól i danych określonych niestandardowych typów błędów, nawet jeśli zostały opakowane dodatkowym kontekstem.
Otrzymasz dwa dane wejściowe:
- Ciąg zawierający szczegóły żądania API w formacie
"endpoint,method,error_code,error_field"(np."/users,POST,400,email") - Ciąg zawierający kontekst systemu w formacie
"service_name,request_id"(np."UserAPI,req_12345")
Twoje zadanie:
- Utwórz niestandardowy typ błędu o nazwie
APIErrorstruct z trzema polami:Code(int) - kod statusu HTTPField(string) - pole, które spowodowało błądMessage(string) - opis błędu
- Zaimplementuj metodę
Error() stringdlaAPIError, która zwraca:"API error [code]: [message] (field: [field])" - Przetwórz pierwsze dane wejściowe, dzieląc je po przecinkach, aby uzyskać endpoint, method, error code i error field
- Przetwórz drugie dane wejściowe, dzieląc je po przecinkach, aby uzyskać service name i request ID
- Przekonwertuj ciąg z kodem błędu na liczbę całkowitą
- Utwórz instancję
APIErrorna podstawie kodu błędu:- Jeśli kod wynosi 400: message
"invalid request data" - Jeśli kod wynosi 401: message
"authentication required" - Jeśli kod wynosi 404: message
"resource not found" - Jeśli kod wynosi 500: message
"internal server error" - Dla dowolnego innego kodu: message
"unknown error"
- Jeśli kod wynosi 400: message
- Utwórz błąd opakowany na poziomie endpointu za pomocą
fmt.Errorfi%w:"[method] [endpoint] failed: %w" - Utwórz opakowany błąd na poziomie usługi:
"[service_name] request [request_id] failed: %w" - Użyj
errors.As, aby wyodrębnićAPIErrorz łańcucha opakowanych błędów i wyświetlić wynik wyodrębniania:- Jeśli wyodrębnianie się powiedzie:
"API error extracted successfully" - Jeśli wyodrębnianie się nie powiedzie:
"Failed to extract API error"
- Jeśli wyodrębnianie się powiedzie:
- Jeśli wyodrębnianie się powiedzie, wyświetl szczegóły wyodrębnionego błędu:
"Extracted Error Details:""Code: [code]""Field: [field]""Message: [message]""Full error: [full_error_message_from_Error_method]"
- Wyświetl cały łańcuch błędów:
"Error Chain:""Original: [original_api_error_message]""Endpoint level: [endpoint_level_error_message]""Service level: [service_level_error_message]"
- Wyświetl końcową analizę:
"Error Analysis:""Service: [service_name]""Request ID: [request_id]""Endpoint: [method] [endpoint]""HTTP Status: [code]""Problem Field: [field]""Error Category: [error_category]", gdzie error_category to "Client Error" dla kodów 400-499, "Server Error" dla kodów 500-599 lub "Unknown" dla innych kodów
Użyj pakietu strings, aby dzielić ciągi wejściowe po przecinkach, pakietu strconv, aby przekonwertować ciąg z kodem błędu na liczbę całkowitą, pakietu errors dla errors.As oraz pakietu fmt do opakowywania błędów. To wyzwanie pokazuje, jak errors.As umożliwia dostęp do konkretnych pól i metod niestandardowych typów błędów w opakowanych łańcuchach błędów, co pozwala na zaawansowaną obsługę i analizę błędów.
Spróbuj swoich sił
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
func main() {
// Wczytaj dane wejściowe
var apiDetails string
var systemContext string
fmt.Scanln(&apiDetails)
fmt.Scanln(&systemContext)
// Przeanalizuj dane wejściowe
apiParts := strings.Split(apiDetails, ",")
endpoint := apiParts[0]
method := apiParts[1]
errorCodeStr := apiParts[2]
errorField := apiParts[3]
contextParts := strings.Split(systemContext, ",")
serviceName := contextParts[0]
requestID := contextParts[1]
// Przekonwertuj kod błędu na liczbę całkowitą
errorCode, _ := strconv.Atoi(errorCodeStr)
// TODO: Napisz swój kod poniżej
// 1. Utwórz strukturę APIError z polami Code, Field i Message
// 2. Zaimplementuj metodę Error() dla APIError
// 3. Utwórz instancję APIError na podstawie kodu błędu
// 4. Utwórz opakowane błędy (na poziomie punktu końcowego i usługi)
// 5. Użyj errors.As, aby wyodrębnić APIError z łańcucha opakowanych błędów
// 6. Wyświetl wynik wyodrębnienia i szczegóły błędu
// 7. Wyświetl łańcuch błędów i końcową analizę
}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