Golangミドルウェアはじめミドルウェア

https://mp.weixin.qq.com/s/-nRWwy8SjW1TlqCglL0CAQ

入門

Web開発の文脈では、「ミドルウェア」は、一般的に「オリジナル包装用途をし、追加機能のアプリケーションの一部を追加します。」を意味します この概念は、常に理解していないようですが、私は、ミドルウェアは素晴らしいことだと思います。

まず第一に、良いプラグイン可能なミドルウェアは、責任を持っており、自給自足です。これは、彼が直接実行することができ、あなたのミドルウェアインタフェースレベルに埋め込むことができることを意味します。それはあなたのエンコーディングではなく、フレームには影響しません、あなたはそれの内部処理層を要求します。あなたのコードを書き換える絶対に必要はありませんあなたはミドルウェアの機能を使用したい場合、あなたは、彼がそこに得るのを助けるあなたが使用したくない場合は、あなたが直接削除することができます挿入することができます。

囲碁の言語を通して、ミドルウェアにも標準ライブラリに、非常に一般的です。初めは、標準ライブラリ関数では、それほど明白ではありませんが、彼らはあなたのハンドラをラップするとき、ネット/ httpまたは我々は外観やミドルウェアを、定義するために何をしたいのTimeoutHandlerと対応する処理要求をStripText、およびいくつかの追加のステップがあります。

最初に、私たちは、ミドルウェアの書き込みが簡単に思えると思いますが、ときに我々は実際にピットの様々な遭遇する書き込み。いくつかの例を見てみましょう。

1、リード要求

この例では、ミドルウェアのすべては、httpを受け取ることになります。パラメータとしてハンドラ、およびhttp.Handlerを返します。これは、一緒に、文字列の中間生成物に、それは非常に簡単になります。当社の製品のすべては、このような基本的なモデルの一つです。

1
2
3
4
5
6
func X(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  // Something here...
  h.ServeHTTP(w, r)
  })
}

私たちは、スラッシュにすべての要求をリダイレクトするようにしたい - と言う/メッセージ/、彼らの非スラッシュと同等の、のような/メッセージ。私たちは書くことができます。

1
2
3
4
5
6
7
8
9
func TrailingSlashRedirect(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.URL.Path != "/" && r.URL.Path[len(r.URL.Path)-1] == '/' {
  http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusMovedPermanently)
  return
  }
  h.ServeHTTP(w, r)
  })
}

何も非常に単純ではありません。

2、変更要求

我々は要求にタイトルを追加したり、それを変更したいとしましょう。http.Handler文書が示されています:

本体の外に読んだことに加えて、ハンドラは、変更要求によって提供されるべきではありません。

標準ライブラリのコピーhttp.Requestを移動します。Requestオブジェクトは対応してチェーンに渡す前に、我々はそうする必要があります。我々は内部のトラックに、各要求に要求IDヘッダーを設定するとします。シャローコピー*リクエストを作成し、代理店の前にタイトルを変更します。

1
2
3
4
5
6
7
8
func RequestID(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  r2 := new(http.Request)
  *r2 = *r
  r2.Header.Set("X-Request-Id", uuid.NewV4().String())
  h.ServeHTTP(w, r2)
  })
}

3、最初の書き込みに応答して

あなたはレスポンスヘッダを設定したい場合は、あなただけの、エージェントの要求をそれらを書くことができます。

1
2
3
4
5
6
func Server(h http.Handler, servername string) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Server", servername)
  h.ServeHTTP(w, r)
  })
}

内部プロセッサは、サーバヘッドを設定した場合上記の質問は、あなたの頭は上書きされますされています。あなたは、内部サーバソフトウェアヘッドを公開したくない場合、またはあなたが問題を引き起こす可能性があり、クライアントに応答を送信する前にヘッドを取り除きたい場合。

これを行うために、我々は彼のResponseWriterインタフェースを実現しなければなりません。ほとんどの時間、我々は唯一のエージェント下をResponseWriterするが、ユーザが応答を書き込もうとした場合、我々はに飛び込むと私たちのタイトルを追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type serverWriter struct {
  w http.ResponseWriter
  name string
  wroteHeaders bool
}
 
func (s *serverWriter) Header() http.Header {
  return s.w.Header()
}
 
func (s *serverWriter) WriteHeader(code int) http.Header {
  if s.wroteHeader == false {
  s.w.Header().Set("Server", s.name)
  s.wroteHeader = true
  }
  s.w.WriteHeader(code)
}
 
func (s *serverWriter) Write(b []byte) (int, error) {
  if s.wroteHeader == false {
  // We hit this case if user never calls WriteHeader (default 200)
  s.w.Header().Set("Server", s.name)
  s.wroteHeader = true
  } return s.w.Write(b)
}

当社のミドルウェアでそれを使用するために、我々は書きます:

1
2
3
4
5
6
7
8
9
func Server(h http.Handler, servername string) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  sw := &serverWriter{
  w: w,
  name: servername,
  }
  h.ServeHTTP(sw, r)
  })
}

問題

如果用户从不调用Write或WriteHeader呢?例如,有一个200状态并且是空body,或者对选项请求的响应——我们的拦截函数都不会运行。因此,我们应该在ServeHTTP调用之后再添加校验。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
func Server(h http.Handler, servername string) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  sw := &serverWriter{
  w: w,
  name: servername,
  }
  h.ServeHTTP(sw, r)
  if sw.wroteHeaders == false {
  s.w.Header().Set("Server", s.name)
  s.wroteHeader = true
  }
  })
}

其他ResponseWriter接口

ResponseWriter接口只需要有三种方法。但在实践中,它也可以对其他接口作出响应,例如http.Pusher。你的中间件可能会意外地禁用HTTP/2支持,这是不好的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Push implements the http.Pusher interface.
func (s *serverWriter) Push(target string, opts *http.PushOptions) error {
  if pusher, ok := s.w.(http.Pusher); ok {
  return pusher.Push(target, opts)
  }
  return http.ErrNotSupported
}
 
// Flush implements the http.Flusher interface.
func (s *serverWriter) Flush() {
  f, ok := s.w.(http.Flusher)
  if ok {
  f.Flush()
  }
}

总结

通过以上的学习,不知道大家对Go编写中间件有没有一个完整的认识。大家也可以尝试着用Go去编写一个中间件。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

 

https://slarker.me/go-middleware/

今天学习了一个之前虽然听过,但是从来没搞懂的概念 — 中间件。这个概念在服务端开发中经常会用到,大的公司也会有专门的团队来做中间件开发,用来提高应用层的开发效率。在 Web 开发中,中间件也就是在请求流程中新增了一层,添加一些额外的功能,比如验证 Token,记录日志等等。一个优秀的中间件能做到可插拔,自我约束,无须重写代码。

在 Go 语言中,实现一个中间件也非常简单,其原理和 Python 中的装饰器非常类似。比如我们想实现一个中间件,用来统计每个接口调用的次数:

package middleware

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func RequestMiddleWare() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		fmt.Println(string(path))
		c.Next()
	}
}

RequestMiddleWare 函数的返回值是一个函数,在函数实现中,可以根据 *gin.Context 拿到请求信息,做一些想要的操作,完成之后可以 c.Next() 执行下一个步骤。整个过程就是在原来的请求处理中新增了一层,非常方便。

除此之外,如果希望在请求结束后还可以做一些操作,可以把相关逻辑放到 c.Next() 之后,当请求结束之后,还会返回来再把中间件中剩余的逻辑执行完毕。也就相当于:

before middleware
    c.Next()
after middleware

 

https://segmentfault.com/a/1190000018819804

中间件是一种计算机软件,可为操作系统提供的软件应用程序提供服务,以便于各个软件之间的沟通,特别是系统软件和应用软件。广泛用于web应用和面向服务的体系结构等。

纵观GO语言,中间件应用比较普遍,主要应用:

  • 记录对服务器发送的请求(request)
  • 处理服务器响应(response )
  • 请求和处理之间做一个权限认证工作
  • 远程调用
  • 安全
  • 等等

中间件处理程序是简单的http.Handler,它包装另一个http.Handler做请求的一些预处理和/或后处理。它被称为“中间件”,因为它位于Go Web服务器和实际处理程序之间的中间位置。

下面是一些中间件例子

记录日志中间件

package main

import (
   "fmt"
   "log"
   "net/http"
)

func logging(f http.HandlerFunc) http.HandlerFunc {
   return func(w http.ResponseWriter, r *http.Request) {
      log.Println(r.URL.Path)
      f(w, r)
   }
}
func foo(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "foo")
}

func bar(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "bar")
}

func main() {
   http.HandleFunc("/foo", logging(foo))
   http.HandleFunc("/bar", logging(bar))
   http.ListenAndServe(":8080", nil)
}

访问 http://localhost:8080/foo

返回结果

foo

将上面示例修改下,也可以实现相同的功能

package main

import (
   "fmt"
   "log"
   "net/http"
)

func foo(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "foo")
}
func bar(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "bar")
}

func loggingMiddleware(next http.Handler) http.Handler {
   return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      log.Println(r.URL.Path)
      next.ServeHTTP(w, r)
   })
}

func main() {

   http.Handle("/foo", loggingMiddleware(http.HandlerFunc(foo)))
   http.Handle("/bar", loggingMiddleware(http.HandlerFunc(bar)))
   http.ListenAndServe(":8080", nil)
}

访问 http://localhost:8080/foo

返回结果

foo

多中间件例子

package main

import (
   "fmt"
   "log"
   "net/http"
   "time"
)

type Middleware func(http.HandlerFunc) http.HandlerFunc

// Logging logs all requests with its path and the time it took to process
func Logging() Middleware {

   // Create a new Middleware
   return func(f http.HandlerFunc) http.HandlerFunc {

      // Define the http.HandlerFunc
      return func(w http.ResponseWriter, r *http.Request) {

         // Do middleware things
         start := time.Now()
         defer func() { log.Println(r.URL.Path, time.Since(start)) }()

         // Call the next middleware/handler in chain
         f(w, r)
      }
   }
}

// Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
func Method(m string) Middleware {

   // Create a new Middleware
   return func(f http.HandlerFunc) http.HandlerFunc {

      // Define the http.HandlerFunc
      return func(w http.ResponseWriter, r *http.Request) {

         // Do middleware things
         if r.Method != m {
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
         }

         // Call the next middleware/handler in chain
         f(w, r)
      }
   }
}

// Chain applies middlewares to a http.HandlerFunc
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
   for _, m := range middlewares {
      f = m(f)
   }
   return f
}

func Hello(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "hello world")
}

func main() {
   http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
   http.ListenAndServe(":8080", nil)
}

中间件本身只是将其http.HandlerFunc作为其参数之一,包装它并返回一个新http.HandlerFunc的服务器来调用。在这里,我们定义了一种新类型Middleware,最终可以更容易地将多个中间件链接在一起。

当然我们也可以改成如下形式

package main

import (
   "fmt"
   "log"
   "net/http"
   "time"
)

type Middleware func(http.Handler) http.Handler

func Hello(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintln(w, "hello world")
}

func Chain(f http.Handler, mmap ...Middleware) http.Handler {
   for _, m := range mmap {
      f = m(f)
   }
   return f
}
func Method(m string) Middleware {
   return func(f http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
         log.Println(r.URL.Path)
         if r.Method != m {
            http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            return
         }
         f.ServeHTTP(w, r)
      })
   }

}
func Logging() Middleware {
   return func(f http.Handler) http.Handler {
      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
         //log.Println(r.URL.Path)
         // Do middleware things
         start := time.Now()
         defer func() { log.Println(r.URL.Path, time.Since(start)) }()
         f.ServeHTTP(w, r)
      })
   }
}

func main() {
   http.Handle("/", Chain(http.HandlerFunc(Hello), Method("GET"), Logging()))
   http.ListenAndServe(":8080", nil)
}

在gin框架下实现中间件
r := gin.Default() 创建带有默认中间件的路由,默认是包含logger和recovery中间件的
r :=gin.new()      创建带有没有中间件的路由

示例

package main

import (
   "github.com/gin-gonic/gin"
   "log"
   "time"
)

func Logger() gin.HandlerFunc {
   return func(c *gin.Context) {
      t := time.Now()
      // Set example variable
      c.Set("example", "12345")
      // before request
      c.Next()
      // after request
      latency := time.Since(t)
      log.Print(latency) //时间  0s
      // access the status we are sending
      status := c.Writer.Status()
      log.Println(status) //状态 200
   }
}
func main() {
   r := gin.New()
   r.Use(Logger())

   r.GET("/test", func(c *gin.Context) {
      example := c.MustGet("example").(string)

      // it would print: "12345"
      log.Println(example)
   })

   // Listen and serve on 0.0.0.0:8080
   r.Run(":8080")
}
以上示例也可改为
package main

import (
   "github.com/gin-gonic/gin"
   "log"
   "time"
)

func Logger() gin.HandlerFunc {
   return func(c *gin.Context) {
      t := time.Now()
      // Set example variable
      c.Set("example", "12345")
      // before request
      c.Next()
      // after request
      latency := time.Since(t)
      log.Print(latency) //时间  0s
      // access the status we are sending
      status := c.Writer.Status()
      log.Println(status) //状态 200
   }
}

func main() {
   r := gin.New()
   r.GET("/test", Logger(), func(c *gin.Context) {
      example := c.MustGet("example").(string)
      // it would print: "12345"
      log.Println(example)
   })
   // Listen and serve on 0.0.0.0:8080
   r.Run(":8080")
}

即不用r.use添加中间件,直接将Logger() 写到r.GET 方法的参数里("/test"之后)。

更多gin中间件示例可参考 https://github.com/gin-gonic/gin

在 Gin 中,接入中间件也非常简单:

router := gin.Default()
router.Use(middleware.RequestMiddleWare())

上面这段代码是在所有的 API 中接入,如果仅仅想在某些 API 上接入,可以使用 Gin 的路由分组:

group := router.Group("/api/v1", middleware.RequestMiddleWare())

おすすめ

転載: www.cnblogs.com/maji233/p/11237349.html