Skip to content
youn edited this page Apr 25, 2021 · 3 revisions

我们在数据库操作的时候,比如 dao 层中当遇到一个 sql.ErrNoRows 的时候,是否应该 Wrap 这个 error,抛给上层。为什么?

答案是需要的。Wrap error 的意义是保留原始错误,可以添加自定义信息,以及保留调用堆栈。
一般第三方lib的error是不保存堆栈的,而和第三方lib打交道的模块就是repository层,所以可以在这一层Warp error一直往上抛,并且在最上层去做error的处理。

sql.ErrNoRows错误?这属于是错误么?如何优雅的处理?

我的想法是可以统一作为错误处理,如果上层业务不觉得sql.ErrNoRows是错误的话,可以判断错误是否是sql.ErrNoRows,如果是的话,就吃掉错误,做正确的处理逻辑。 这个是repository层的处理逻辑。

repository的error需要关注业务错误码吗?

我觉得是可以的。这样做的好处是,可以统一在repository层去关注错误码。而不需要在上层去做错误的判断,再去做业务错误码处理。而且,如果repository返回多种类型的错误,都需要上层去做多次错误判断,这样是不太好的。

func (u *UserRepository) Get(ctx context.Context, id int64) (user domain.User, err error) {
	tx := u.db.WithContext(ctx)
	err = tx.First(&user, id).Error
        if err != nil {
            if errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.Wrap(UserNotFoundError, fmt.Sprintf("get user failed. id: %d. \n err: %s \n", id, err.Error()))
	    } else {
		err = errors.Wrap(UserUnkonwError, fmt.Sprintf("get user failed. id: %d. \n err: %s \n", id, err.Error()))
	    }
	}
	return
}

那么最上层如何处理跑出来的错误呢?

当server层拿到这个错误后,可以使用erorrs.As(err, &myErr{}),转化自己error,然后取出里面的code,message去做接口的响应。

func Render(c *gin.Context, err error) {
    var myErr = &MyErr{}
    if errors.As(err, myErr) {
       // todo
       c.String(myErr.Code, myErr.Message)
    } else {
       c.String(500, err.Error())
    }
}