Menu
CoddyTech

Bọc lỗi với fmt.Errorf

Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 54 trên 107.

Khi lỗi đi qua nhiều lớp trong ứng dụng, việc biết lỗi bắt nguồn ở đâu trở nên rất quan trọng. Hàm (function) fmt.Errorf của Go với động từ %w cho phép bạn bọc lỗi, giữ nguyên lỗi ban đầu đồng thời bổ sung ngữ cảnh.

Việc bọc lỗi (error wrapping) tạo thành một chuỗi lỗi. Mỗi lớp có thể bổ sung thông tin về việc nó đang cố làm gì khi lỗi xảy ra:

func ReadConfig(filename string) error {
    data, err := os.ReadFile(filename)
    if err != nil {
        return fmt.Errorf("reading config file: %w", err)
    }
    // xử lý dữ liệu...
    return nil
}

Động từ %w có chức năng đặc biệt. Nó bọc lỗi ban đầu bên trong lỗi mới. Khác với %v, chỉ chuyển lỗi thành chuỗi và làm mất danh tính của lỗi ban đầu.

func LoadSettings() error {
    err := ReadConfig("settings.json")
    if err != nil {
        return fmt.Errorf("loading settings: %w", err)
    }
    return nil
}

Khi LoadSettings thất bại, thông báo lỗi hiển thị toàn bộ chuỗi: "loading settings: reading config file: open settings.json: no such file or directory". Mỗi lớp bổ sung ngữ cảnh, giúp việc gỡ lỗi dễ dàng hơn nhiều.

Lỗi được bọc vẫn chứa lỗi gốc bên trong. Trong bài học tiếp theo, bạn sẽ học cách mở bọc các lỗi này và kiểm tra bên trong bằng errors.Is() và errors.As().

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống xử lý đơn hàng để minh họa cách bọc lỗi qua nhiều lớp. Bạn sẽ tạo một chuỗi hàm (function), trong đó mỗi lớp bổ sung ngữ cảnh cho lỗi, giúp dễ dàng xác định chính xác vấn đề xảy ra ở đâu.

Bạn sẽ sắp xếp code trong hai tệp:

  • orders.go: Tạo logic xử lý đơn hàng với nhiều lớp bọc lỗi khi lỗi truyền lên các lớp phía trên.

    Triển khai ba hàm tạo thành một chuỗi xử lý:

    • ValidateItem(itemID string) error - Trả về một lỗi với thông báo "item not found" nếu itemID là "INVALID", nếu không thì trả về nil
    • ProcessOrder(orderID, itemID string) error - Gọi ValidateItem. Nếu hàm này trả về lỗi, hãy bọc lỗi đó với ngữ cảnh: "processing order [orderID]: %w". Nếu không thì trả về nil
    • SubmitOrder(customerName, orderID, itemID string) error - Gọi ProcessOrder. Nếu hàm này trả về lỗi, hãy bọc lỗi đó với ngữ cảnh: "submitting order for [customerName]: %w". Nếu không thì trả về nil

    Mỗi lớp nên dùng fmt.Errorf với động từ %w để bọc lỗi từ lớp bên dưới, tạo thành một chuỗi ngữ cảnh.

  • main.go: Đọc thông tin đơn hàng từ đầu vào, gọi SubmitOrder và hiển thị kết quả. Nếu xảy ra lỗi, hãy in toàn bộ chuỗi lỗi. Nếu thành công, hãy in thông báo xác nhận.

Chương trình sẽ nhận các đầu vào sau:

  • Dòng 1: Tên khách hàng
  • Dòng 2: Mã đơn hàng
  • Dòng 3: Mã mặt hàng

In kết quả:

  • Nếu xảy ra lỗi: Error: [full error chain]
  • Nếu thành công: Order [orderID] submitted successfully for [customerName]

Ví dụ, với các đầu vào Alice, ORD-123 và INVALID, đầu ra của bạn sẽ là:

Error: submitting order for Alice: processing order ORD-123: item not found

Hãy chú ý cách thông báo lỗi hiển thị toàn bộ chuỗi. Bạn có thể lần theo vấn đề từ thao tác gửi đơn hàng ở lớp cao nhất, qua bước xử lý đơn hàng, đến lỗi xác thực thực tế. Mỗi lớp bổ sung ngữ cảnh riêng bằng %w.

Với các đầu vào Bob, ORD-456 và ITEM-001, đầu ra của bạn sẽ là:

Order ORD-456 submitted successfully for Bob

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Đọc tên khách hàng
	scanner.Scan()
	customerName := scanner.Text()
	
	// Đọc ID đơn hàng
	scanner.Scan()
	orderID := scanner.Text()
	
	// Đọc ID mặt hàng
	scanner.Scan()
	itemID := scanner.Text()
	
	// TODO: Gọi SubmitOrder với các giá trị đầu vào
	// TODO: Nếu xảy ra lỗi, in ra: Error: [full error chain]
	// TODO: Nếu thành công, in ra: Order [orderID] submitted successfully for [customerName]
	
	_ = customerName
	_ = orderID
	_ = itemID
}
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 Lập trình hướng đối tượng

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