常见处理
项目中比较常见的err处理方式如下:
package main
import "os"
func main() {
f, err := os.Open("test.go")
if err != nil {
panic(err)
}
//这个一定要放到if下面,如果open执行失败,那么f有可能为nil,nil就没有close这个方法
defer f.Close()
//其他代码 懒得写
}
error类型
在Go中避免不了针对error类型来打交道,error底层就是一个interface
//代码位于 GOROOT/src/builtin/builtin.go L:307
type error interface {
Error() string
}
自定义项目中的error常量
可以看到error就是一个interface,只要实现一下这个interface就可以在项目中做一些错误常量的定义
示例代码
package load
const ErrorOpenFile = ioError("open file error")
// 定义一个类型,实现error接口
type ioError string
// 逻辑很简单直接返回原因即可
func (e ioError) Error() string { return string(e) }
func ErrorCheck(err error) error {
if err != nil {
return ErrorOpenFile
}
return nil
}
调用
package main
import (
"demo/load"
"fmt"
)
func main() {
fmt.Println(load.ErrorCheck(nil) == load.ErrorOpenFile)
fmt.Println(load.ErrorCheck(fmt.Errorf("sss")) == load.ErrorOpenFile)
}
error链路跟踪
当项目中的代码层级比较多的时候,当前层不能处理当前错误,逐层向上反直到某一层可以对该错误进行处理
models层
package models
var dbConn *gorm.DB
const ErrorUserExist = userError("用户已存在")
// 定义一个用户的错误类型,实现error接口
type userError string
// 逻辑很简单直接返回原因即可
func (e userError) Error() string { return string(e) }
type User struct {
Id int64 `json:"id" gorm:"column:id;primaryKey"`
Username string `json:"username" gorm:"column:username" binding:"required"`
Password string `json:"password" gorm:"column:password"`
Email string `json:"email" gorm:"column:email"`
}
func (u User) TableName() string {
return "user"
}
// gorm钩子函数,保存之前会调度一下这个方法
func (u *User) BeforeSave(tx *gorm.DB) (err error) {
// 检测一下数据库表里面有没有这个用户
if u.checkUserExistence(u.Id, u.Username) {
return UserExist
}
return nil
}
func (u User) SaveUser(user User) (int64, error) {
if err := dbConn.Save(&user).Error; err != nil {
return 0, err
}
return user.Id, nil
}
func (u User) checkUserExistence(id int64, username string) bool {
var user User
err := dbConn.Where("id != ?", id).
Where("username = ?", username).
Find(&user).Error
if err != nil || user.Id == 0 {
return false
}
return true
}
调度层
package main
import (
"demo/models"
"errors"
"fmt"
)
func main() {
err := SaveUserToDb()
if err != nil {
if errors.Is(err, models.ErrorUserExist) {
fmt.Println("用户名已经存在了")
} else {
fmt.Println("其他错误:", err)
}
}
}
func SaveUserToDb() error {
_, err := (models.User{}).SaveUser(models.User{Id: 1, Username: "test"})
if err != nil {
return fmt.Errorf("save db err:%w", err)
}
// 假装这里还有点逻辑处理
return nil
}
解读
SaveUserToDb这个函数是处理用户数据保存入库,main为我们的控制器层,当在处理数据库保存的时候发现dao层写数据库出现了错误,在SaveUserToDb这一层我们不知道怎么处理这个错误,可以使用fmt.Errorf %w将这个错误包一层向上返回%w错误处理文档,当逻辑逐步向上返回到控制器层时,发现当前层可以处理这个错误了,就可以具体辨别一下这个错误具体是什么,然后去做一些针对性的处理
error格式简化
示例代码
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
demo("tcp", "127.0.0.1:6666")
}
func demo(network, address string) error {
conn, err := net.Dial(network, address)
if err != nil {
return fmt.Errorf("demo %s %s err:%v", network, address, err)
}
_, err = conn.Write([]byte("ping"))
if err != nil {
return fmt.Errorf("demo %s %s err:%v", network, address, err)
}
r := bufio.NewReader(conn)
status, err := r.ReadString('\n')
if err != nil {
return fmt.Errorf("demo %s %s err:%v", network, address, err)
}
if status == "ok" {
_, err := conn.Write([]byte("hello"))
if err != nil {
return fmt.Errorf("demo %s %s err:%v", network, address, err)
}
}
return nil
}
打印简化
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
fmt.Println(demo("tcp", "127.0.0.1:6666"))
}
func demo(network, address string) (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("demo %s %s err:%v", network, address, err)
}
}()
conn, err := net.Dial(network, address)
if err != nil {
return
}
_, err = conn.Write([]byte("ping"))
if err != nil {
return
}
r := bufio.NewReader(conn)
status, err := r.ReadString('\n')
if err != nil {
return
}
if status == "ok" {
_, err = conn.Write([]byte("ping"))
if err != nil {
return
}
}
return nil
}
参考资料:
github.com/golang/go/w…