带你撸出一手好代码
Golang http处理函数中不修改旧代码注入新逻辑的方法

golang语法相对简陋,对于习惯使用Java、 php之类的c++系面向对象编程语言的程序员而言,某些原本亲轻而易举能实现的功能在golang中要绕一个大圈子或者多出许多冗余代码才能完成。

 

比如一个非常常见的需求: 给所有管理后台的访问链接加上身份验证,阻止非法访问。

 

在Java或php中完成这项功能基本上不用改动原有代码,因为所有这些需要加身份验证的链接通常会被放在一个类里面或则一系列继承至某一个父类的类里面。

 

如果是前一种情况只需要将身份验证的逻辑放在类的构造方法中即可

 

如果是后一种情况可以将验证逻辑放至父类的构造方法

 

因为验证逻辑需要在页面响应前被执行,而构造方法会在类初始化时执行,自然先一步于类中其它代码被执行,因此将验证逻辑放在构造方法中恰好能完成任务。

 

然而,golang虽能模拟继承,但是没有语言内置的构造函数。 我们需要一种机制让验证身份的代码先于功能代码被执行。

 

golang的http请求处理代码通常是一个路由对应一个函数

 

http.HandleFunc("/admin/article_list",  admin.ArticleList)
http.HandleFunc("/admin/article_add", admin.ArticleAdd)
http.HandleFunc("/admin/article_del", admin.ArticleDel )
 
func ArticleList(response http.ResponseWriter, request *http.Request) {
        //代码逻辑
      response.Write("结果")
}
 
func ArticleAdd(response http.ResponseWriter, request *http.Request) {
        //代码逻辑
       response.Write("结果")
}
 
 
func ArticleDel(response http.ResponseWriter, request *http.Request) {
        //代码逻辑
    response.Write("结果")
}


 如果要为每个函数增加身份验证逻辑,在不使用特殊编码技巧的情况下,无可避免的需要修改所有已有的函数逻辑,将身份验证代码插入其中

 

func ArticleList(response http.ResponseWriter, request *http.Request) {
        if(!isLogin(response ,request))  {
           return
        }
        //代码逻辑
        response.Write("结果")
}


这样做的缺点很明显,身份验证代码会分布在所有的有身份验证需求的函数中,即使验证逻辑被提取至某个函数,但是调用验证逻辑的代码无论如何也无法被省略。 所以,这种解决问题的方法并不优雅。

 

我们急需一种机制可以将身份验证代码注入至功能代码之前。

 

一种方法是建立一个工厂函数,工厂函数接收的参数便是功能函数,最终工厂函数会返回这个功能函数,但在返回之前会先执行身份验证逻辑

 

func AdminHandleFuncFactory( handleFunc func(response http.ResponseWriter, request *http.Request) )  func(response http.ResponseWriter, request *http.Request) {
        if(!isLogin(response ,request))  {
               return
        }
        return handleFunc 
}

http.HandleFunc("/admin/article_list", AdminHandleFuncFactory(admin.ArticleList))


然而, 这种思路虽好,代码却无法按照思路被执行。因为验证登录的逻辑依赖request和repsonse两个变量。

 

http.HandleFunc("/admin/article_list", AdminHandleFuncFactory(admin.ArticleList))


而这句代码被执行时这两个变量根本不存在,request和response要等到http请求到达时才会产生。 这条路似乎走不通。

 

然而,还是这种思路,只要稍微变通一下, 难题就能迎刃而解。

 

我们可以在这个工厂函数再加一个中间层

 

func AdminHandleFuncFactory( handleFunc func(response http.ResponseWriter, request *http.Request) )  func(response http.ResponseWriter, request *http.Request) {
    return func(response http.ResponseWriter, request *http.Request) {
        if !isLogin(response,request) {
            response.Write(LoginErrorResponse())
            return
        }
        handleFunc(response,request)
    }
}


工厂函数并不直接返回功能函数, 而是返回一个新的和功能函数规格一致的http请求处理函数,可以把这个函数看成是一个装饰函数,与功能函数有相同的表征,但是扩展了其功能,也就是增加了身份验证的功能。问题因此迎刃而解。

作者:陈大侠
日期:2018-02-12

留言(0条)

我要发表留言

您的大名 选填
电子邮箱 选填

欢迎关注微信公众号 「带你撸出一手好代码」

首页    GitHub 知乎 豆瓣 博客园