Menu
CoddyTech

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.

challenge icon

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à:

  1. Tạo một kiểu lỗi tùy chỉnh có tên là APIError struct với ba trường:
    • Code (int), mã trạng thái HTTP
    • Field (string), trường gây ra lỗi
    • Message (string), mô tả lỗi
  2. Triển khai phương thức Error() string cho APIError, trả về: "API error [code]: [message] (field: [field])"
  3. 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
  4. 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
  5. Chuyển chuỗi mã lỗi thành số nguyên
  6. Tạo một instance APIError dự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"
  7. Tạo một lỗi được bọc ở cấp endpoint bằng fmt.Errorf với %w: "[method] [endpoint] failed: %w"
  8. Tạo một lỗi được bọc ở cấp dịch vụ: "[service_name] request [request_id] failed: %w"
  9. Dùng errors.As để trích xuất APIError từ 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"
  10. 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]"
  11. 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]"
  12. 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
	
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Go online