更好的错误处理

Dr.*_*eel 3 error-handling web-applications http go

在这里https://github.com/astaxie/build-web-application-with-golang/blob/master/en/11.1.md描述了如何根据http包增强自定义路由器和自定义错误类型的错误处理。

type appError struct {
    Error   error
    Message string
    Code    int
}    

type appHandler func(http.ResponseWriter, *http.Request) *appError
// custom handler catching errors
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if e := fn(w, r); e != nil { // e is *appError, not os.Error.
        c := appengine.NewContext(r)
        c.Errorf("%v", e.Error)
        http.Error(w, e.Message, e.Code)
    }
}
// fetch data or return *appError
func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        return &appError{err, "Record not found", 404}
    }
    if err := viewTemplate.Execute(w, record); err != nil {
        return &appError{err, "Can't display record", 500}
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

目的是使所有处理程序在发生错误的情况下都返回* appError并将其写入路由器的响应中,因此无需在代码中直接调用c.JSON(500,err)viewRecord

Gin怎么做?

Dr.*_*eel 6

这里 @manucorporat杜松子酒开发商说:

我们鼓励开发人员使用中间件来处理错误响应,以便他们可以将错误逻辑与常规流逻辑分开。

在Gin中实现集中式错误处理,您应该使用.Use(Middleware),在路径处理程序的代码中使用gin.Context.Error()将错误信息附加到请求上下文。中间件知道gin.Context.Errors,在这里您可以阅读它们并根据需要进行处理gin.Context.Next()

下面的代码。

处理Gin中间件时出错:

//
// APP error definition
//
type appError struct {
    Code     int    `json:"code"`
    Message  string `json:"message"`
}

//
// Middleware Error Handler in server package
//
func JSONAppErrorReporter() gin.HandlerFunc {
    return jsonAppErrorReporterT(gin.ErrorTypeAny)
}

func jsonAppErrorReporterT(errType gin.ErrorType) gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()
        detectedErrors := c.Errors.ByType(errType)

        log.Println("Handle APP error")
        if len(detectedErrors) > 0 {
            err := detectedErrors[0].Err
            var parsedError *appError
            switch err.(type) {
            case *appError:
                parsedError = err.(*appError )
            default:
                parsedError = &appError{ 
                  code: http.StatusInternalServerError,
                  message: "Internal Server Error"
                }
            }
            // Put the error into response
            c.IndentedJSON(parsedError.Code, parsedError)
            c.Abort()
            // or c.AbortWithStatusJSON(parsedError.Code, parsedError)
            return
        }

    }
}

//
//  Report Error in app
//
func fetchSingleHostGroup(c *gin.Context) {
    hostgroupID := c.Param("id")

    hostGroupRes, err := getHostGroupResource(hostgroupID)

    if err != nil {
        // put the Error to gin.Context.Errors
        c.Error(err)
        return
    }
    // return data of OK
    c.JSON(http.StatusOK, *hostGroupRes)
}
//
// Server setup
//
func main() {
    router := gin.Default()
    router.Use(JSONAppErrorReporter())
    router.GET("/hostgroups/:id", fetchSingleHostGroup)
    router.Run(":3000")
}
Run Code Online (Sandbox Code Playgroud)

可以在以下位置找到另一个错误处理思路: