Chuyển tới nội dung chính

Xử lý Lỗi trong Go - Cách xử lý lỗi thông minh! 🚨

Chào mừng bạn đến với bài học về Xử lý Lỗi trong Go! Trong bài học này, chúng ta sẽ tìm hiểu cách xử lý lỗi một cách hiệu quả và an toàn.

Lỗi là gì? 🤔

Lỗi là những tình huống không mong muốn xảy ra khi chương trình đang chạy. Ví dụ:

  • Không tìm thấy file
  • Kết nối database thất bại
  • Dữ liệu không hợp lệ

💡 Ví dụ thực tế:

  • Giống như khi bạn mở cửa nhưng chìa khóa không vừa
  • Hoặc khi bạn gọi điện nhưng số máy không tồn tại
  • Cần xử lý những tình huống này một cách phù hợp

Xử lý Lỗi Cơ Bản 🎯

1. Tạo lỗi

func chia(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("không thể chia cho 0")
}
return a / b, nil
}

💡 Giải thích:

  • errors.New: Tạo lỗi mới
  • nil: Không có lỗi
  • Luôn trả về cả kết quả và lỗi

2. Kiểm tra lỗi

func main() {
ketQua, err := chia(10, 0)
if err != nil {
fmt.Println("Lỗi:", err)
return
}
fmt.Println("Kết quả:", ketQua)
}

💡 Giải thích:

  • Luôn kiểm tra err != nil
  • Xử lý lỗi trước khi tiếp tục
  • In thông báo lỗi rõ ràng

Lỗi Tùy Chỉnh 🎨

1. Tạo kiểu lỗi mới

type LoiXacThuc struct {
Truong string
ThongBao string
}

func (e *LoiXacThuc) Error() string {
return fmt.Sprintf("%s: %s", e.Truong, e.ThongBao)
}

💡 Giải thích:

  • Tạo struct riêng cho lỗi
  • Thêm thông tin chi tiết
  • Định nghĩa cách hiển thị lỗi

2. Sử dụng lỗi tùy chỉnh

func kiemTraNguoiDung(nguoiDung NguoiDung) error {
if nguoiDung.Ten == "" {
return &LoiXacThuc{
Truong: "Ten",
ThongBao: "tên không được để trống",
}
}
return nil
}

💡 Giải thích:

  • Trả về lỗi tùy chỉnh
  • Thêm thông tin chi tiết
  • Dễ dàng xử lý sau này

Bọc Lỗi (Error Wrapping) 📦

1. Sử dụng fmt.Errorf

func docFile(duongDan string) error {
duLieu, err := ioutil.ReadFile(duongDan)
if err != nil {
return fmt.Errorf("không thể đọc file %s: %w", duongDan, err)
}
return nil
}

💡 Giải thích:

  • fmt.Errorf: Tạo lỗi với thông tin bổ sung
  • %w: Bọc lỗi gốc
  • Giữ lại thông tin lỗi chi tiết

2. Mở bọc lỗi

func main() {
err := docFile("cauHinh.json")
if err != nil {
if errors.Is(err, os.ErrNotExist) {
fmt.Println("File không tồn tại")
}
}
}

💡 Giải thích:

  • errors.Is: Kiểm tra loại lỗi
  • Xử lý lỗi cụ thể
  • Giữ lại thông tin chi tiết

Các Mẫu Xử lý Lỗi 🎯

1. Xử lý lỗi với defer

func xuLyFile(duongDan string) error {
file, err := os.Open(duongDan)
if err != nil {
return err
}
defer file.Close() // Đóng file khi kết thúc

// Xử lý file
return nil
}

💡 Giải thích:

  • defer: Đảm bảo tài nguyên được giải phóng
  • Xử lý lỗi sớm
  • Code sạch và an toàn

2. Xử lý lỗi với panic

func batBuoc(err error) {
if err != nil {
panic(err)
}
}

💡 Giải thích:

  • panic: Dừng chương trình khi lỗi nghiêm trọng
  • Chỉ sử dụng cho lỗi không thể khôi phục
  • Cẩn thận khi sử dụng

Best Practices (Cách sử dụng tốt nhất) ✅

  1. Luôn kiểm tra lỗi

    // ✅ Đúng
    ketQua, err := motHam()
    if err != nil {
    return err
    }

    // ❌ Sai
    ketQua := motHam() // Không kiểm tra lỗi
  2. Xử lý lỗi sớm

    // ✅ Đúng
    func xuLy() error {
    if err := buoc1(); err != nil {
    return err
    }
    return buoc2()
    }

    // ❌ Sai
    func xuLy() error {
    if err := buoc1(); err != nil {
    // Xử lý lỗi phức tạp
    }
    return buoc2()
    }
  3. Thêm thông tin chi tiết

    // ✅ Đúng
    return fmt.Errorf("lỗi xác thực cho người dùng %s: %w", maNguoiDung, err)

    // ❌ Sai
    return errors.New("lỗi xác thực")

Xử lý Lỗi trong HTTP 🌐

1. Xử lý lỗi HTTP

func xuLyYeuCau(w http.ResponseWriter, r *http.Request) {
duLieu, err := layDuLieu()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.Write(duLieu)
}

💡 Giải thích:

  • Gửi mã lỗi HTTP phù hợp
  • Trả về thông báo lỗi rõ ràng
  • Dừng xử lý khi có lỗi

2. Lỗi HTTP tùy chỉnh

type LoiHTTP struct {
MaLoi int
ThongBao string
}

func (e *LoiHTTP) Error() string {
return e.ThongBao
}

func xuLyLoi(w http.ResponseWriter, err error) {
if loiHTTP, ok := err.(*LoiHTTP); ok {
http.Error(w, loiHTTP.ThongBao, loiHTTP.MaLoi)
return
}
http.Error(w, "Lỗi Máy Chủ Nội Bộ", http.StatusInternalServerError)
}

💡 Giải thích:

  • Tạo lỗi HTTP tùy chỉnh
  • Xử lý lỗi theo loại
  • Trả về mã lỗi phù hợp

Xử lý Lỗi trong Database 💾

1. Xử lý lỗi database

func (db *DB) LayNguoiDung(id string) (*NguoiDung, error) {
var nguoiDung NguoiDung
err := db.QueryRow("SELECT * FROM nguoi_dung WHERE id = ?", id).Scan(&nguoiDung)
if err != nil {
if err == sql.ErrNoRows {
return nil, &LoiKhongTimThay{ID: id}
}
return nil, fmt.Errorf("lỗi database: %w", err)
}
return &nguoiDung, nil
}

💡 Giải thích:

  • Xử lý lỗi không tìm thấy
  • Bọc lỗi database
  • Trả về thông tin chi tiết

2. Xử lý lỗi giao dịch

func (db *DB) ChuyenTien(tu, den string, soTien float64) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback() // Hoàn tác nếu có lỗi

// Thực hiện chuyển tiền

return tx.Commit()
}

💡 Giải thích:

  • Sử dụng giao dịch
  • Hoàn tác khi có lỗi
  • Xác nhận khi thành công

Ghi Log Lỗi 📝

1. Ghi log lỗi có cấu trúc

type LogLoi struct {
ThoiGian time.Time
Loi error
ThongTin map[string]interface{}
}

func ghiLogLoi(err error) {
log := LogLoi{
ThoiGian: time.Now(),
Loi: err,
ThongTin: make(map[string]interface{}),
}
// Ghi log vào file hoặc dịch vụ
}

💡 Giải thích:

  • Ghi log có cấu trúc
  • Thêm thông tin chi tiết
  • Dễ dàng phân tích

2. Theo dõi lỗi

func theoDoiLoi(err error) {
if err != nil {
// Gửi đến dịch vụ theo dõi lỗi
sentry.CaptureException(err)
}
}

💡 Giải thích:

  • Theo dõi lỗi từ xa
  • Phát hiện vấn đề sớm
  • Cải thiện độ tin cậy

Tiếp theo 🎯

Trong các bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về testing
  • Học cách debug
  • Khám phá các công cụ phân tích lỗi
  • Thực hành với các dự án thực tế

💡 Lời khuyên: Hãy nghĩ về xử lý lỗi như việc xử lý các tình huống bất ngờ trong cuộc sống. Cần phải chuẩn bị và xử lý một cách thông minh để đảm bảo mọi thứ hoạt động tốt!