引子

GinEcho都是我很喜欢的web框架。

它们2个对于Handler函数的定义略有不同.

  • Gin

    type HandlerFunc func(*Context)
    
  • Echo

    type HandlerFunc func(Context) error
    

HandlerFunc定义不同, Echo比Gin多返回了error。

写Restful业务时,一般正常情况下返回数据,错误情况下需要返回错误号,或者一个错误对象,该对象包含错误号和错误消息或其他信息. 我们看下在Gin框架下如何构建。

Gin

先定义Error,Resonses数据结构,然后定义response函数.

type Error struct {
    HttpCode int         `json:"-"`
    Code     int         `json:"code"` // 这个是业务层的错误号,非Http Status Code
    Reason   interface{} `json:"reason"`
}

type Response struct {
    Error    *Error      
    Content  interface{}
}

func response(c *gin.Context, rsp *Response) {
    if rsp.Error!=nil {
        c.JSON(rsp.Error.HttpCode, rsp.Error)
        return
    }
    c.JSON(rsp.Content)
}

Handler使用:

func GetXXX(c *gin.Context) {
    rsp := &Response{}

    defer response(rsp)

    xxx, err := xxxSvc.Get()
    if err!=nil {
        rsp.Error = &Error{HttpCode: http.StatusInternalServerError, Code:1000, Reason:err}
        return
    }
    rsp.Content = xxx
    return
}

这里要注意的是defer, 千万不能写成:

var rsp Response
defer response(&rsp)

defer在处理时,会先对函数参数求值,如此一来,rsp里都是0值,无论之后rsp再如何赋值,最终response处理的都是0值。

可以像上述那样使用指针,也可以使用匿名函数来wrap一下:

var rsp Response
defer func() {
    response(&rsp)
}()

Echo

Echo的HandlerFunc返回error,当然我们可以不理会这个error, 那么和Gin的处理方式一样了。

当然也可以利用这个error,如果我们返回error,Echo能够按照我们的协议输出error信息,正常的信息归正常的信息返回,岂不美哉.

先定义错误对象

type Error struct {
    HttpCode int         `json:"-"`
    Code     int         `json:"code"` // 这个是业务层的错误号,非Http Status Code
    Reason   interface{} `json:"reason"`
}

首先要做的,是看下Echo能否支持自定义的错误处理,只有支持了,我们才能截获错误,并按照我们的要求输出。

// 自定义错误处理函数
func ErrorHandle(err error, c echo.Context) {
	switch e := err.(type) {
	case *Error:
		code := e.HTTPCode
		c.JSON(code, e)
	default:
		c.Echo().DefaultHTTPErrorHandler(err, c)
	}
}

// 替换默认的错误处理
e := echo.New()
e.HTTPErrorHandler = ErrorHandle

有了错误对象和自定义的错误处理,来看下在Handler里如何使用

func GetXXX(c echo.Context) error {
    xxx, err:= xxxSvc.Get()
    if err!=nil {
        return &Error{HttpCode: http.StatusInternalServerError, Code:1000, Reason:err}
    }
    return c.JSON(http.StatusOK, xxx)
}

利用了error以后,HandlerFunc的代码会简洁很多,而且符合go-style的错误处理方式.