如何妥善处理gin / golang Web服务中的错误

Mad*_*bat 1 go

我正在使用gin编写一个简单的REST API。我已经阅读了许多有关使出错处理在go中不再重复的文章和文章,但是我似乎无法全神贯注于如何在gin处理程序中进行错误处理。

我所做的所有工作就是对数据库运行一些查询,并将结果返回为JSON,因此典型的处理程序如下所示

func DeleteAPI(c *gin.Context) {
    var db = c.MustGet("db").(*sql.DB)
    query := "DELETE FROM table WHERE some condition"
    tx, err := db.Begin()
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    defer tx.Rollback()
    result, err := tx.Exec(query)
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    num, err := result.RowsAffected()
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    err = tx.Commit()
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    c.JSON(200, gin.H{"deleted": num})
}
Run Code Online (Sandbox Code Playgroud)

如您所见,即使这个简单的处理程序也会重复执行相同的“ if err!= nil”模式四次。在基于“选择”的API中,我有两倍多,因为绑定输入数据时可能存在错误,而将响应编组到JSON时则存在错误。有什么办法可以使它更干吗?

Fli*_*mzy 5

我通常的方法是使用包装函数。这样做的优点是(相对于Adrian的答案,这也是一个不错的选择,顺便说一句),将错误处理保留为Go惯用的形式(of return result, err,而不是用handleError(err)类型调用来填充代码),同时仍将其合并为一个位置。

func DeleteAPI(c *gin.Context) {
    num, err := deleteAPI(c)
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    c.JSON(200, gin.H{"deleted": num})
}

func deleteAPI(c *gin.Context) (int, error) {
    var db = c.MustGet("db").(*sql.DB)
    query := "DELETE FROM table WHERE some condition"
    tx, err := db.Begin()
    if err != nil {
        return 0, err
    }
    defer tx.Rollback()
    result, err := tx.Exec(query)
    if err != nil {
        return 0, err
    }
    num, err := result.RowsAffected()
    if err != nil {
        return 0, err
    }
    err = tx.Commit()
    if err != nil {
        return 0, err
    }
    return num, nil
}
Run Code Online (Sandbox Code Playgroud)

对我(通常对Go编码器而言),其优先级是代码可读性高于DRY。在我看来,在这三个选项(您的原始选项,Adrian的选项和我的选项)中,我的版本更具可读性,原因很简单,即错误是完全以惯用的方式处理的,并且它们冒泡到顶级处理程序。如果您的控制器最终调用其他返回错误的函数,则此方法同样有效。通过将所有错误处理移至最顶层的函数,您在所有其余代码中都将摆脱错误处理的混乱(简单的'if err!= nil {return err}`结构除外)。

还值得注意的是,通过更改“包装”函数,此方法可以与Adrian结合使用,特别是与多个处理程序一起使用时,功能强大:

func DeleteAPI(c *gin.Context) {
    result, err := deleteAPI(c)
    if handleError(c, err) {
        return
    }
    c.JSON(200, gin.H{"deleted": num})
}
Run Code Online (Sandbox Code Playgroud)