【问题标题】:Golang idiomatic error handlingGolang 惯用的错误处理
【发布时间】:2014-01-21 17:47:11
【问题描述】:

我为独立和嵌入式使用(例如使用 CustomerRepository)创建了一个“基础”存储库结构,以避免一直检查错误,并为 Gorp(数据库工具包)创建一个抽象,并创建一个 API更符合我的喜好。

我检查这个基本结构中的错误,如果发现一个错误,就会恐慌,就好像我认为确实存在一个错误,然后表明一个开发错误,代码也可能恐慌,因为验证等应该在数据获取之前发生到存储库。

我发现了这个问题Go Error Handling Techniques,但它不包括像我所做的那样将错误包装在基本结构中并且只是恐慌。

我所做的是否是惯用的 Go?

package repositories

import (
    "github.com/coopernurse/gorp"
)

type Repository struct {
    Gorp gorp.SqlExecutor
}

func (r *Repository) GetById(i interface{}, id int) interface{} {
    obj, err := r.Gorp.Get(i, id)
    if err != nil {
        panic(err)
    }
    return obj
}

func (r *Repository) Get(holder interface{}, query string, args ...interface{}) interface{} {
    if err := Gorp.SelectOne(holder, query, args); err != nil {
        panic(err)
    }
}

func (r *Repository) Select(i interface{}, query string, args ...interface{}) {
    if _, err := Gorp.Select(holder, query, args); err != nil {
        panic(err)
    }
}

func (r *Repository) Insert(list ...interface{}) {
    if err := r.Gorp.Insert(list...); err != nil {
        panic(err)
    }
}

func (r *Repository) Update(list ...interface{}) int64 {
    count, err := r.Gorp.Update(list...)
    if err != nil {
        panic(err)
    }
    return count
}

func (r *Repository) Delete(list ...interface{}) int64 {
    count, err := r.Gorp.Delete(list...)
    if err != nil {
        panic(err)
    }
    return count
}

【问题讨论】:

    标签: go


    【解决方案1】:

    不要惊慌,这不是 Go 的方式。相反,做这样的事情--

    func (r *Repository) GetById(i interface{}, id int) (interface{}, error) {
        obj, err := r.Gorp.Get(i, id)
        if err != nil {
            return nil, err
        }
        return obj, nil
    }
    

    然后只需处理调用者中的错误。我从上面的 cmets 中看到,您在 Martini 处理程序中使用这些函数,所以您会执行这样的操作 --

    func MyHandler(parameters) (int, string) {
        obj, err := repository.GetById(something, id)
        if err == repository.ErrNotFound {
            return http.StatusNotFound, fmt.Sprintf("could not find by id: %d", id)
        }
        if err != nil {
            return http.StatusInternalError, err.Error()
        }
        return http.StatusOk, fmt.Printf("obj: %v", obj)
    }
    

    这更像是 Go 方式。确保 r.Gorp.Get 确实返回您在包中声明的特定错误。

    var ErrNotFound = errors.New("not found")
    

    尽可能多地创建对您的代码有意义的代码。

    【讨论】:

    • 这正是我想要的。 panic() 在教程中的流行程度是不对的。
    【解决方案2】:

    惯用的方法是返回带有关联类型值的错误,即

    func (list ...interface{}) (v int46, err error) {}
    

    ...然后检查 err != nil 调用这些函数的位置。

    最终使用 panic() 将导致类似异常的错误处理和更多样板代码(如果您认为错误是可恢复的)。

    惯用的错误处理在 Go 中很冗长,但不如模拟异常(这根本不是“Go 方式”)。

    【讨论】:

    • 所以错误应该根据具体情况进行评估?我不太确定这些错误是否可恢复,好像 GetById 失败,则实体不存在,如果删除失败,则由于编码错误(例如未正确处理外键等)而出现非常错误的情况。
    • 我不打算从它们中恢复,我打算制造一个 NotFoundError、NotAuthorisedError 等等,只是“恐慌”它们,并在我的 web 应用程序中使用 Martini 中间件拦截这些错误并显示404 页面,或 403 页面,500 页面等等。
    • 我想是的,作为最佳实践 - 所有错误都不相等,就像在您自己的示例中,我将为 GetById() 定义 ErrEntityNotFound = errors.New("entity not found")。如果您正在构建 API,Delete() 可能无法执行删除同一实体的顺序请求 - 这值得“恐慌”吗?在我看来,您应该从返回错误中确定错误级别。把你所有的玩具都扔出婴儿车似乎不是一个合乎逻辑的处理方式。
    猜你喜欢
    • 2017-01-31
    • 1970-01-01
    • 2018-11-18
    • 2019-02-26
    • 1970-01-01
    • 2018-08-08
    • 2015-02-28
    • 1970-01-01
    • 2017-04-10
    相关资源
    最近更新 更多