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ớinil
: 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) ✅
-
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 -
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()
} -
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!