Go http客户端有“中间件”吗?

时间:2016-09-16 09:19:23

标签: go

我想问一下我们是否可以为Go http客户端创建'中间件'功能?示例我想添加一个日志函数,因此将记录每个发送的请求,或添加setAuthToken,以便将令牌添加到每个请求的标题中。

4 个答案:

答案 0 :(得分:10)

您可以使用HTTP客户端中的Transport参数,使用组合模式,使用以下事实:

  • http.Client.Transport定义将处理所有HTTP请求的函数;
  • http.Client.Transport的接口类型为http.RoundTripper,因此可以替换为您自己的实现;

例如:

package main

import (
    "fmt"
    "net/http"
)

// This type implements the http.RoundTripper interface
type LoggingRoundTripper struct {
    Proxied http.RoundTripper
}

func (lrt LoggingRoundTripper) RoundTrip(req *http.Request) (res *http.Response, e error) {
    // Do "before sending requests" actions here.
    fmt.Printf("Sending request to %v\n", req.URL)

    // Send the request, get the response (or the error)
    res, e = lrt.Proxied.RoundTrip(req)

    // Handle the result.
    if (e != nil) {
        fmt.Printf("Error: %v", e)
    } else {
        fmt.Printf("Received %v response\n", res.Status)
    }

    return
}

func main() {
    var c = &http.Client{Transport:LoggingRoundTripper{http.DefaultTransport}}
    c.Get("https://www.google.com")
}

随意改变你想要的名字,我很久没想到了。

答案 1 :(得分:0)

这可以使用闭包功能来实现。通过一个例子可能更清楚:

package main

import (  
  "fmt"
  "net/http"
)

func main() {  
  http.HandleFunc("/hello", logged(hello))
  http.ListenAndServe(":3000", nil)
}

func logged(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {  
  return func(w http.ResponseWriter, r *http.Request) {
    fmt.Println("logging something")
    f(w, r)
    fmt.Println("finished handling request")
  }
}

func hello(w http.ResponseWriter, r *http.Request) {  
  fmt.Fprintln(w, "<h1>Hello!</h1>")
}

信用额度为:http://www.calhoun.io/5-useful-ways-to-use-closures-in-go/

答案 2 :(得分:0)

我写了一个小型的教程/库来https://github.com/HereMobilityDevelopers/mediary

这是一些基本用法示例:

client := mediary.Init().AddInterceptors(dumpInterceptor).Build()
client.Get("https://golang.org")

func dumpInterceptor(req *http.Request, handler mediary.Handler) (*http.Response, error) {
    if bytes, err := httputil.DumpRequestOut(req, true); err == nil {
        fmt.Printf("%s", bytes)

        //GET / HTTP/1.1
        //Host: golang.org
        //User-Agent: Go-http-client/1.1
        //Accept-Encoding: gzip
    }
    return handler(req)
}

https://github.com/HereMobilityDevelopers/mediary/wiki/Reasoning

也有解释

答案 3 :(得分:0)

好主意!这是Go中HTTP服务中间件的简单实现。 通常,一个简单的http服务框架是注册一堆路由,然后根据路由调用不同的逻辑来对其进行处理。

但是实际上,可能存在一些涉及几乎所有路由的统一处理,例如日志,权限等。

因此,此时最好进行中间预处理。

  1. 定义一个中间件单元:
package main

import (
    "net/http"
)

// AdaptorHandle middleware func type
type AdaptorHandle func(w http.ResponseWriter, r *http.Request) (next bool, err error)

// MiddleWareAdaptor router middlewares mapped by url
type MiddleWareAdaptor struct {
    URLs map[string][]AdaptorHandle
}

// MakeMiddleWareAdaptor make a middleware adaptor
func MakeMiddleWareAdaptor() *MiddleWareAdaptor {
    mwa := &MiddleWareAdaptor{
        URLs: make(map[string][]AdaptorHandle),
    }

    return mwa
}

// Regist regist a adaptor
func (mw *MiddleWareAdaptor) Regist(url string, Adaptor ...AdaptorHandle) {
    for _, adp := range Adaptor {
        mw.URLs[url] = append(mw.URLs[url], adp)
        // mw.URLs[url] = adp
    }
}

// Exec exec middleware adaptor funcs...
func (mw *MiddleWareAdaptor) Exec(url string, w http.ResponseWriter, r *http.Request) (bool, error) {
    if adps, ok := mw.URLs[url]; ok {
        for _, adp := range adps {
            if next, err := adp(w, r); !next || (err != nil) {
                return next, err
            }
        }
    }
    return true, nil
}
  1. 然后用中间件条目包装路由处理功能:
func middlewareHandler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // before call handler
        start := time.Now()
        do, _ := mwa.Exec(r.URL.Path, w, r) // exec middleware
        // call next handler
        if do {
            log.Println("middleware done. next...")
            next.ServeHTTP(w, r)
        } else {
            log.Println("middleware done.break...")
        }
        // after call handle
        log.Printf("Comleted %s in %v", r.URL.Path, time.Since(start))
    })
}

mux.Handle("/", middlewareHandler(&uPlusRouterHandler{}))

type uPlusRouterHandler struct {
}

func (rh *uPlusRouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
}
  1. 最后,注册所需的中间件:
mwa = MakeMiddleWareAdaptor() // init middleware
mwa.Regist("/", testMWAfunc, testMWAfunc2) // regist middleware
...
func testMWAfunc(w http.ResponseWriter, r *http.Request) (bool, error) {
    log.Println("I am Alice Middleware...")
    log.Printf("Started %s %s", r.Method, r.URL.Path)
    return true, nil
}

func testMWAfunc2(w http.ResponseWriter, r *http.Request) (bool, error) {
    log.Println("I am Ben Middleware...")
    return false, nil // return false,break follow-up actions.
}