大奖18dj18vip-大奖18dj18娱乐官网

【腾讯云】云产品限时秒杀,爆款1核2G云服务器,首年99元

大奖18dj18vip

查看: 14|回复: 0
打印 上一主题 下一主题

[资讯] 用 20 行代码写出清晰易用的 Go 中间件 API

[复制链接]
  • TA的每日心情
    擦汗
    前天 13:47
  • 签到天数: 659 天

    [LV.9]以坛为家II

    硕士生

    1万

    主题

    1万

    帖子

    4万

    积分

    Rank: 8Rank: 8

    UID
    15343
    威望
    -561
    贡献
    8107
    在线时间
    337 小时
    注册时间
    2015-10-12
    跳转到指定楼层
    楼主
    发表于 2020-6-28 13:36:11 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

    在使用 Go 编写复杂的服务时,您将遇到一个典型的主题是中间件。这个话题在网上被讨论了一次又一次。本质上,中间件允许我们做了如下事情:

    • 拦截 ServeHTTP 调用,执行任意代码
    • 对调用链(Continuation Chain) 上的请求/响应流进行更改
    • 打断中间件链,或继续下一个中间件**并最终到达真正的请求处理器

    这些与 express.js 中间件所做的工作非常类似。我们探索了各种库,找到了接近我们想要的现有解决方案,但是他们要么有不要的额外内容,要么不符合我们的品位。显然,我们可以在 express.js 中间件的启发下,写出 20 行代码以下的更清晰的易用的 API(Installation API)

    抽象

    在设计抽象时,我们首先设想如何编写中间件函数(下文开始称为**),答案非常明显:

    • func NewElapsedTimeInterceptor() MiddlewareInterceptor {
    • return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    • startTime := time.Now()
    • defer func() {
    • endTime := time.Now()
    • elapsed := endTime.Sub(startTime)
    • // 记录时间消耗
    • }()
    • next(w, r)
    • }
    • }
    • func NewRequestIdInterceptor() MiddlewareInterceptor {
    • return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
    • if r.Headers.Get("X-Request-Id") == "" {
    • r.Headers.Set("X-Request-Id", generateRequestId())
    • }
    • next(w, r)
    • }
    • }

    它们看起来就像 http.HandlerFunc,但有一个额外的参数 next,该函数(参数)会继续处理请求链。这将允许任何人像编写类似 http.HandlerFunc 的简单函数一样写**,它可以拦截调用,执行所需操作,并在需要时传递控制权。

    接下来,我们设想如何将这些**连接到 http.Handler 或 http.HandlerFunc 中。为此,首先要定义 MiddlewareHandlerFunc,它只是 http.HandlerFunc 的一种类型。(type MiddlewareHandlerFunc http.HandlerFunc)。这将允许我们在 http.HandlerFunc 栈上之上构建一个更好的 API。现在给定一个 http.HandlerFunc 我们希望我们的链式 API 看起来像这样:

    • func HomeRouter(w http.ResponseWriter, r *http.Request) {
    • // 处理请求
    • }
    • // ...
    • // 在程序某处注册 Hanlder
    • chain := MiddlewareHandlerFunc(HomeRouter).
    • Intercept(NewElapsedTimeInterceptor()).
    • Intercept(NewRequestIdInterceptor())
    • // 像普通般注册 HttpHandler
    • mux.Path("/home").HandlerFunc(http.HandlerFunc(chain))

    将 http.HandlerFunc 传递到 MiddlewareHandlerFunc,然后调用 Intercept 方法注册我们的 Interceptor。Interceptor 的返回类型还是 MiddlewareHandlerFunc,它允许我们再次调用 Intercept。

    使用 Intercept 组合需要注意的一件重要事情是执行的顺序。由于 chain(responseWriter, request)是间接调用最后一个**,**的执行是反向的,即它从尾部的**一直返回到头部的处理程序。这很有道理,因为你在拦截调用时,**应该要在真正的请求处理器之前执行。

    简化

    虽然这种反向链系统使抽象更加流畅,但事实证明,大多数情况下 s 我们有一个预编译的**数组,能够在不同的 handlers 之间重用。同样,当我们将中间件链定义为数组时,我们自然更愿意以它们执行顺序声明它们(而不是相反的顺序)。让我们将这个数组**称为中间件链。我们希望我们的中间件链看起来有点像:

    • // 调用链或中间件可以按下标的顺序执行
    • middlewareChain := MiddlewareChain{
    • NewRequestIdInterceptor(),
    • NewElapsedTimeInterceptor(),
    • }
    • // 调用所有以 HomeRouter 结尾的中间件
    • mux.Path("/home").Handler(middlewareChain.Handler(HomeRouter))
    实现

    一旦我们设计好抽象的概念,实现就显得简单多了

    • package middleware
    • import "net/http"
    • // MiddlewareInterceptor intercepts an HTTP handler invocation, it is passed both response writer and request
    • // which after interception can be passed onto the handler function.
    • type MiddlewareInterceptor func(http.ResponseWriter, *http.Request, http.HandlerFunc)
    • // MiddlewareHandlerFunc builds on top of http.HandlerFunc, and exposes API to intercept with MiddlewareInterceptor.
    • // This allows building complex long chains without complicated struct manipulation
    • type MiddlewareHandlerFunc http.HandlerFunc
    • // Intercept returns back a continuation that will call install middleware to intercept
    • // the continuation call.
    • func (cont MiddlewareHandlerFunc) Intercept(mw MiddlewareInterceptor) MiddlewareHandlerFunc {
    • return func(writer http.ResponseWriter, request *http.Request) {
    • mw(writer, request, http.HandlerFunc(cont))
    • }
    • }
    • // MiddlewareChain is a collection of interceptors that will be invoked in there index order
    • type MiddlewareChain []MiddlewareInterceptor
    • // Handler allows hooking multiple middleware in single call.
    • func (chain MiddlewareChain) Handler(handler http.HandlerFunc) http.Handler {
    • curr := MiddlewareHandlerFunc(handler)
    • for i := len(chain) - 1; i >= 0; i-- {
    • mw := chain
    • curr = curr.Intercept(mw)
    • }
    • return http.HandlerFunc(curr)
    • }

    因此,在不到 20 行代码(不包括注释)的情况下,我们就能够构建一个很好的中间件库。它几乎是简简单单的,但是这几行连贯的抽象实在是太棒了。它使我们能够毫不费力地编写一些漂亮的中间件链。希望这几行代码也能激发您的中间件体验。

    大奖18dj18vip社区温馨提示:
    大奖18dj18vip(www.dastanona.com)十分重视网络版权及其他知识产权的保护,针对网络侵权采取如下版权政策:
    1、大奖18dj18vip有理由相信网友侵犯任何人的版权或作品,(图文,文字,下载,视频,非法传播),大奖18dj18vip有权不事先通知即删除涉嫌侵权的作品和内容
    2、大奖18dj18vip将采取必要的网络技术手段,确认为侵权作品或内容的用户有权进行警告、屏蔽、删除的行为,尽可能的防止侵权行为的发生
    3、大奖18dj18vip影视资源均收集自互联网,没有提供影片资源存储,也未参与录制上传,若大奖18dj18vip收录的资源涉及您的版权或知识产权或其他利益,我们会立即删除
    4、大奖18dj18vip,删帖,投诉,举报,侵权,若大奖18dj18vip侵犯您的权益,附上身份及权利证明,请直接发送邮件到 kefu-sosoba@qq.com 我们将在一个工作日内删除
    soso大奖18dj18vip社区是聚合百度搜索,搜狗搜索,360搜索,新闻,教育,站长,广告,娱乐,影视,微信,网盘,营销,手机,汽车,游戏,论坛综合为一体的大型门户社区www.dastanona.com
    【腾讯云】中小企业福利专场,多款刚需产品,满足企业通用场景需求,云服务器2.5折起
    Powered by www.dastanona.com Copyright © 2013-2020 大奖18dj18vip社区 小黑屋|手机版|地图|关于我们|腾讯云代金券|帮助中心|公共DNS|大奖18dj18vip
    广告服务/项目合作: kefu-sosoba@qq.com  侵权举报邮箱: kefu-sosoba@qq.com  大奖18dj18vip建站时间:创建于2013年07月23日
    免责声明:大奖18dj18vip所有的内容均来自互联网以及第三方作者自由发布,版权归原作者版权所有,大奖18dj18vip不承担任何的法律责任,若有侵权请来信告知,我们立即删除!

    GMT+8, 2020-7-16 07:12 , Processed in 0.057552 second(s), 10 queries , MemCache On.

    快速回复 返回顶部 返回列表