zoukankan      html  css  js  c++  java
  • Golang 如何统一处理HTTP请求中的异常捕获

    最近写go,路由选择httprouter,现在希望在不修改httprouter源码的前提下,对所有注册的路由handle进行异常捕获。

    golang使用panic()产生异常,然后可以recover()来捕获到异常,否则主程序直接宕掉,这是我们不希望看到的。
    或者全程检查error,不主动抛出异常。即便这样,可能异常依然不能避免。

    func RegRouters(r *httprouter.Router) {
    	r.GET("/", Home)
    	r.GET("/contact", Contact)
    }
    
    func Home(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
    	defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Printf("panic recover: %v
    ", pr)
    				debug.PrintStack()
    			}
    		}()
    	//something
    }
    
    func Contact(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
    	defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Printf("panic recover: %v
    ", pr)
    				debug.PrintStack()
    			}
    		}()
    	//something
    }
    

    正常的路由表长这样,在C#中相当于对每个请求加try...catch,将业务上代码包裹起来。
    golang同理:

    func RegRouters(r *httprouter.Router) {
    	r.GET("/", WrapHandle(Home))
    	r.GET("/contact", WrapHandle(Contact))
    }
    

    WrapHandle需要有一个handle类型的参数,同时返回值要能被httprouter接收

    func WrapHandle(handle httprouter.Handle) httprouter.Handle {
    	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    		defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Printf("panic recover: %v
    ", pr)
    				debug.PrintStack()
    			}
    		}()
    		handle(w, r, p)
    	}
    }
    

    加一个上下文,把handle简化一下

    func WrapHandle(handle func(ctx *context.Context)) httprouter.Handle {
    	return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
    		defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Println("panic recover: %v", pr)
    				debug.PrintStack()
    			}
    		}()
    		ctx := context.NewContext(w, r, p)
    		handle(ctx)
    	}
    }
    func Home(ctx *context.Context) {
    	defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Printf("panic recover: %v
    ", pr)
    				debug.PrintStack()
    			}
    		}()
    	//something
    }
    
    func Contact(ctx *context.Context) {
    	defer func() {
    			if pr := recover(); pr != nil {
    				fmt.Printf("panic recover: %v
    ", pr)
    				debug.PrintStack()
    			}
    		}()
    	//something
    	id = ctx.FormInt64("id") //通过context从FORM取值,并转换为int64
    }
    

    这里加了一个context包,handle的参数也减少到一个,主要是context上可以做更多的事情。

    一个简单的请求上下文的原型:

    type Context struct {
    	responseWriter http.ResponseWriter
    	request        *http.Request
    	params         httprouter.Params
    	Data           map[string]interface{}
    }
    
    func NewContext(w http.ResponseWriter, r *http.Request, params httprouter.Params) *Context {
    	ctx := new(Context)
    	ctx.responseWriter = w
    	ctx.request = r
    	ctx.params = params
    	ctx.Data = make(map[string]interface{})
    	return ctx
    }
    
    func (ctx *Context) FormValue(name string) string {
    	return ctx.request.FormValue(name)
    }
    
    func (ctx *Context) FormInt64(name string) int64 {
    	value, _ := strconv.ParseInt(ctx.FormValue(name), 10, 64)
    	return value
    }
    
    //更多扩展....
    

    好了,先到这里,希望对你有帮助。

  • 相关阅读:
    Vue-router(5)之 路由的before家族
    Vue-router(4)之路由跳转
    Vue-router(3)之 router-link 和 router-view 使用
    Vue-router(1)之component标签
    Vue.js(4)- 生命周期
    Vue.js 之 过渡动画
    Vue.js(2)- 过滤器
    函数节流和函数防抖
    认识与学习 BASH
    linux下创建文件与目录时默认被赋予了什么样的权限?
  • 原文地址:https://www.cnblogs.com/ronli/p/golang-wraphandle.html
Copyright © 2011-2022 走看看