在Go中编写复杂的服务时,您会遇到的一个典型主题是中间件。这个话题在网上被反复讨论过。本质上,中间件允许我们做这样的事情:拦截ServeHTTP调用,执行任意代码以更改ContinuationChain上的请求/响应流,打断中间件链,或继续下一个中间件拦截器并最终获取实际请求handler这与express.js中间件所做的非常相似。我们探索了各种库并找到了接近我们想要的现有解决方案,但它们要么有不需要的额外功能,要么不符合我们的口味。显然,在express.js中间件的启发下,我们可以用不到20行代码编写出更清晰易用的API(InstallationAPI)抽象。在设计抽象的时候,我们首先想象如何编写中间件函数(以下简称拦截器),答案很明显:=time.Now()deferfunc(){endTime:=time.Now()elapsed:=endTime.Sub(startTime)//记录耗时}()next(w,r)}}funcNewRequestIdInterceptor()MiddlewareInterceptor{returnfunc(whttp.ResponseWriter,r*http.Request,nexthttp.HandlerFunc){ifr.Headers.Get("X-Request-Id")==""{r.Headers.Set("X-Request-Id",generateRequestId())}next(w,r)}}它们看起来像http.HandlerFunc,但是接下来多了一个参数,这个函数(参数)会继续处理请求链。这将允许任何人编写一个拦截器,就好像它是一个像http.HandlerFunc这样的简单函数,它可以拦截调用,执行所需的操作,并在需要时传递控制权。接下来,我们设想如何将这些拦截器连接到http.Handler或http.HandlerFunc中。为此,首先定义MiddlewareHandlerFunc,它只是一种http.HandlerFunc。(类型MiddlewareHandlerFunchttp.HandlerFunc)。这将使我们能够在http.HandlerFunc堆栈之上构建更好的API。现在给定一个http.HandlerFunc,我们希望我们的链式API看起来像这样:MiddlewareHandlerFunc(HomeRouter).Intercept(NewElapsedTimeInterceptor()).Intercept(NewRequestIdInterceptor())//照常注册HttpHandlermux.Path("/home").HandlerFunc(http.HandlerFunc(chain))并通过http.HandlerFunc去MiddlewareHandlerFunc,然后调用拦截方法来注册我们的拦截器。Interceptor的返回类型仍然是MiddlewareHandlerFunc,这样我们就可以再次调用Intercept。使用拦截组合时要注意的一件重要事情是执行顺序。由于chain(responseWriter,request)是间接调用最后一个拦截器,所以拦截器的执行是相反的,即从尾部的拦截器一路返回到头部的处理程序。这是有道理的,因为当您拦截调用时,拦截器应该在实际请求处理程序之前执行。简化虽然这个后链系统使抽象更平滑,但事实证明,大多数时候我们有一个预编译的拦截器数组,可以在不同的处理程序中重用。此外,当我们将中间件链定义为数组时,我们自然更喜欢按照它们执行的顺序(而不是相反的顺序)来声明它们。我们将这个拦截器数组称为中间件链。我们希望我们的中间件链看起来有点像://调用链或者中间件可以按照下标顺序执行middlewareChain:=MiddlewareChain{NewRequestIdInterceptor(),NewElapsedTimeInterceptor(),}//调用所有以HomeRouter.Path结尾的中间件("/home").Handler(middlewareChain.Handler(HomeRouter))implementation一旦我们设计了抽象的概念,实现就变得简单多了//ThisallowsbuildingcomplexlongchainswithoutcomplicatedstructmanipulationtypeMiddlewareHandlerFunchttp.HandlerFunc//Intercept返回一个continuationthatwillcalallinstallmiddlewaretointercept//thecontinuationcall.func(contMiddlewareHandlerFunc)Intercept(mwMiddlewareInterceptor)MiddlewareHandlerFunc{returnfunc(writerhttp.ResponseWriter,request*http.Request){mw(writer,request,http.HandlerFunc(cont))}}//MiddlewareChain是拦截器的集合,thatwillbeinvokedinthereindexordertypeMiddlewareChain[]MiddlewareInterceptor//Handlerallowshookingmultiplemiddlewareinsinglecall.func(chainMiddlewareChain)Handler(handlerhttp.HandlerFunc)http.Handler=curr:(handler)fori:=len(chain)-1;i>=0;i--{mw:=chain[i]curr=curr.Intercept(mw)}returnhttp.HandlerFunc(curr)}因此,在小于在20行代码(不包括注释)中,我们能够构建一个不错的中间件库。这几乎是微不足道的,但是这几行连贯的抽象非常棒。它允许我们毫不费力地编写一些不错的中间件链。希望这几行代码也能激发您的中间件体验。
