Gỡ bọc lỗi bằng `errors.As`
Thuộc phần Logic và luồng điều khiển trong hành trình GO của Coddy. Bài học 41 trên 68.
Trong khi errors.Is giúp bạn xác định các giá trị lỗi cụ thể trong chuỗi, đôi khi bạn cần truy cập các trường và dữ liệu thực tế của một kiểu lỗi tùy chỉnh. Hàm errors.As cung cấp khả năng này bằng cách tìm lỗi đầu tiên trong chuỗi khớp với một kiểu cụ thể và trích xuất lỗi đó để kiểm tra.
Hàm errors.As nhận hai tham số: lỗi cần kiểm tra và con trỏ đến một biến có kiểu lỗi mục tiêu. Nếu tìm thấy kiểu lỗi khớp trong chuỗi lỗi, hàm gán lỗi đó cho biến của bạn và trả về true:
var validationErr ValidationError
if errors.As(wrappedErr, &validationErr) {
fmt.Printf("Field: %s, Message: %s\n", validationErr.Field, validationErr.Message)
}Điều này đặc biệt hữu ích khi làm việc với các kiểu lỗi tùy chỉnh có thêm thông tin ngữ cảnh. Không giống errors.Is chỉ kiểm tra tính bằng nhau, errors.As trích xuất chính đối tượng lỗi, cho phép bạn truy cập các trường và phương thức của nó. Nhờ vậy, việc xử lý lỗi trở nên tinh vi và giàu thông tin hơn trong các ứng dụng phức tạp.
Thử thách
DễXây dựng một hệ thống xử lý lỗi API dùng errors.As để trích xuất và kiểm tra các kiểu lỗi tùy chỉnh từ chuỗi lỗi được bọc. Thử thách này minh họa cách dùng errors.As để truy cập các trường và dữ liệu của những kiểu lỗi tùy chỉnh cụ thể, ngay cả khi chúng được bọc thêm ngữ cảnh.
Bạn sẽ nhận hai đầu vào:
- Một chuỗi chứa thông tin chi tiết về yêu cầu API theo định dạng
"endpoint,method,error_code,error_field"(ví dụ:"/users,POST,400,email") - Một chuỗi chứa ngữ cảnh hệ thống theo định dạng
"service_name,request_id"(ví dụ:"UserAPI,req_12345")
Nhiệm vụ của bạn là:
- Tạo một kiểu lỗi tùy chỉnh có tên là
APIErrorstruct với ba trường:Code(int), mã trạng thái HTTPField(string), trường gây ra lỗiMessage(string), mô tả lỗi
- Triển khai phương thức
Error() stringchoAPIError, trả về:"API error [code]: [message] (field: [field])" - Phân tích đầu vào thứ nhất bằng cách tách theo dấu phẩy để lấy endpoint, method, mã lỗi và trường lỗi
- Phân tích đầu vào thứ hai bằng cách tách theo dấu phẩy để lấy tên dịch vụ và request ID
- Chuyển chuỗi mã lỗi thành số nguyên
- Tạo một instance
APIErrordựa trên mã lỗi:- Nếu mã là 400: thông báo
"invalid request data" - Nếu mã là 401: thông báo
"authentication required" - Nếu mã là 404: thông báo
"resource not found" - Nếu mã là 500: thông báo
"internal server error" - Với mọi mã khác: thông báo
"unknown error"
- Nếu mã là 400: thông báo
- Tạo một lỗi được bọc ở cấp endpoint bằng
fmt.Errorfvới%w:"[method] [endpoint] failed: %w" - Tạo một lỗi được bọc ở cấp dịch vụ:
"[service_name] request [request_id] failed: %w" - Dùng
errors.Asđể trích xuấtAPIErrortừ chuỗi lỗi được bọc và hiển thị kết quả trích xuất:- Nếu trích xuất thành công:
"API error extracted successfully" - Nếu trích xuất thất bại:
"Failed to extract API error"
- Nếu trích xuất thành công:
- Nếu trích xuất thành công, hiển thị chi tiết lỗi đã trích xuất:
"Extracted Error Details:""Code: [code]""Field: [field]""Message: [message]""Full error: [full_error_message_from_Error_method]"
- Hiển thị toàn bộ chuỗi lỗi:
"Error Chain:""Original: [original_api_error_message]""Endpoint level: [endpoint_level_error_message]""Service level: [service_level_error_message]"
- Hiển thị phần phân tích cuối cùng:
"Error Analysis:""Service: [service_name]""Request ID: [request_id]""Endpoint: [method] [endpoint]""HTTP Status: [code]""Problem Field: [field]""Error Category: [error_category]", trong đó error_category là "Client Error" với mã từ 400 đến 499, "Server Error" với mã từ 500 đến 599, hoặc "Unknown" với các mã khác
Dùng gói strings để tách các chuỗi đầu vào theo dấu phẩy, gói strconv để chuyển chuỗi mã lỗi thành số nguyên, gói errors cho errors.As và gói fmt để bọc lỗi. Thử thách này minh họa cách errors.As giúp bạn truy cập các trường và phương thức cụ thể của những kiểu lỗi tùy chỉnh bên trong chuỗi lỗi được bọc, qua đó hỗ trợ xử lý và phân tích lỗi nâng cao.
Tự làm thử
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
func main() {
// Đọc đầu vào
var apiDetails string
var systemContext string
fmt.Scanln(&apiDetails)
fmt.Scanln(&systemContext)
// Phân tích đầu vào
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]
// Chuyển mã lỗi thành số nguyên
errorCode, _ := strconv.Atoi(errorCodeStr)
// TODO: Viết code bên dưới
// 1. Tạo struct APIError với các trường Code, Field, Message
// 2. Triển khai phương thức Error() cho APIError
// 3. Tạo instance APIError dựa trên mã lỗi
// 4. Tạo các lỗi được bọc ở cấp endpoint và cấp dịch vụ
// 5. Dùng errors.As để trích xuất APIError từ chuỗi lỗi được bọc
// 6. Hiển thị kết quả trích xuất và chi tiết lỗi
// 7. Hiển thị chuỗi lỗi và phân tích cuối cùng
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Logic và luồng điều khiển
1Luồng điều khiển nâng cao
Switch với `fallthrough`Thoát khỏi vòng lặp lồng nhauTiếp tục một vòng lặp cụ thểCâu lệnh `goto`Ôn tập: điều khiển vòng lặp nâng cao4Dự án: Danh sách việc cần làm đơn giản
Thiết lập dự ánThêm một việc cần làm7Xử lý lỗi chuyên sâu
Kiểu lỗi tùy chỉnhBọc lỗi bằng `%w`Gỡ bọc lỗi bằng `errors.Is`Gỡ bọc lỗi bằng `errors.As`Tìm hiểu `panic`Sử dụng `recover`Ôn tập: phép chia an toàn2Struct và phương thức
Định nghĩa phương thức cho structReceiver giá trịReceiver con trỏChọn receiverPhương thức và hàmÔn tập: hành vi của struct5Tìm hiểu chuyên sâu về map
Map chứa structCon trỏ làm giá trị của mapKiểm tra map nilSo sánh các mapÔn tập: bộ đếm tần suất từ8Dự án: Quản lý hàng tồn kho đơn giản
Tổng quan dự ánKiểm tra hàng tồn kho3Interface (cơ bản)
Interface là gì?Định nghĩa interfaceTriển khai interfaceDùng kiểu interfaceInterface rỗngKhẳng định kiểuType switchÔn tập: hình dạng và hành viTự luyện tập: Trình biên dịch Go online