golang web框架 utron 的异常统一处理

虽然 golang 提供了函数返回 Error 的方式帮助我们安全地检查函数调用是否出错,但是有些运行时的 panic 还是没办法抓到。我们希望能够像 Spring 的 @ControllerAdvice 注解的类,实现里面的 @ExceptionHandler 注解的方法,就可以抓住所有的 Exception 包括 UncheckedException。

通过 debug,我们找到了 routes.go 文件,里面的 wrapController() 方法是处理所有 http 请求的入口,原来的代码如下:

// wrapController wraps a controller ctrl with method fn, and returns http.HandleFunc
func (r *Router) wrapController(ctx *base.Context, fn string, ctrl controller.Controller) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		r.handleController(ctx, fn, ctrl)
	}
}

这里的 handleController() 方法实现,并没有很好的处理策略,只是返回 http Status 500。

// executes the method fn on Controller ctrl, it sets context.
func (r *Router) handleController(ctx *base.Context, fn string, ctrl controller.Controller) {
	ctrl.New(ctx)
	// execute the method
	// TODO: better error handling?
	if x := ita.New(ctrl).Call(fn); x.Error() != nil {
		ctx.Set(http.StatusInternalServerError)
		_, _ = ctx.Write([]byte(x.Error().Error()))
		ctx.TextPlain()
		_ = ctx.Commit()
		return
	}
	err := ctx.Commit()
	if err != nil {
		//TODO:  Log error
		ctx.Log.Errors("=============\n%s===========\n",err.Error())
	}
}

我们前后端是分离的,前端希望收到统一格式的 JSON,格式大概是:

{"Code":"FAIL","Msg":"We caught a panic","Data":"invalid memory address or nil pointer dereference"}
我们的做法也简单,就是在 wrapController() 里面加上一个 defer 函数,专门抓取所有从 handleController() 里面抛出的异常,代码如下:

func (r *Router) wrapController(ctx *base.Context, fn string, ctrl controller.Controller) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				json.NewEncoder(w).Encode(
					controller.ResultJson{
						"FAIL",
						"We caugth a panic",
						err,
					}, 
				)
			}
		}()

		r.handleController(ctx, fn, ctrl)
	}
}

ResultJson 是我们定义的一个 Struct, 里面包含了 Code, Msg, Data 三个属性。


你可能感兴趣的:(Golang)