当你正在构建一个Web应用程序有可能要运行许多(甚至全部)的HTTP请求一些共享功能,你可能想记录每一个request,gzip压缩的每个response,或者做一些繁重的处理或者缓存检查。
实现这个共享功能的一种方法是将其设置为中间件,他可以作为一个独立的程序,在正常的handlers处理之前。根本不需要重写代码:如果你想用一个中间件,就把它加上应用中;如果你改变主意了,去掉就好了。就这么简单。
- ServeMux => Middleware Handler => Application Handler
这篇文章,我会给大家介绍怎么自己去实现一个自定义的middleware模式。以及通过使用第三方的中间件软件包的一些具体的实例。
基本原则:
在Go语言中实现和使用middleware是非常简单的。
使我们的中间件能搞满足 http.handlers 这个接口
建立一个 handlers 链,使其能够满足中间件的 handler 和 正常应用的 handler,并且能够注册到 http.ServeMux
我来解释如何实现:
首先你要知道go 的http handle,这里假设你是知道的
- func messageHandler(message string) http.Handler {
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
- w.Write([]byte(message)
-
- })
-
- }
这上面这个代码片段里面我们的逻辑很简单只是一个简单的 w.Write() 然后我们使用 http.HandlerFunc 适配器来转化这个闭包,并返回。
我们可以使用一个相同的方法来创建一个 handler 链。可以使用 handler 代替参数 string 传进闭包,然后把控制 handler 给传进来的 handler,并且调用 ServeHTTP() 方法。
这给了我们一个完整的模式构建中间件:
- func exampleMiddleware(next http.Handler) http.Handler {
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-
-
- next.ServeHTTP(w, r)
-
- })
-
- }
你注意到这个中间件有一个这样的函数结构 func(http.Handler) http.Handler 。它接受一个 handler 作为参数,并且返回一个 handler。这里有两个很有用的原因:
因为这个函数返回一个句柄可以直接供中间件注册
我们可以建立任意长度的 handler 链来通过中间件的方法互相嵌套
比如:
- http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
控制流说明:
让我们来看一个带有多个中间件的例子,并且把日志输出到控制台:
- package main
-
- import (
-
- "log"
-
- "net/http"
-
- )
-
- func middlewareOne(next http.Handler) http.Handler {
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
- log.Println("Executing middlewareOne")
-
- next.ServeHTTP(w, r)
-
- log.Println("Executing middlewareOne again")
-
- })
-
- }
-
- func middlewareTwo(next http.Handler) http.Handler {
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
- log.Println("Executing middlewareTwo")
-
- if r.URL.Path != "/" {
-
- return
-
- }
-
- next.ServeHTTP(w, r)
-
- log.Println("Executing middlewareTwo again")
-
- })
-
- }
-
- func final(w http.ResponseWriter, r *http.Request) {
-
- log.Println("Executing finalHandler")
-
- w.Write([]byte("OK"))
-
- }
-
- func main() {
-
- finalHandler := http.HandlerFunc(final)
-
- http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
-
- http.ListenAndServe(":3000", nil)
-
- }
然后我们执行 go run main.go 在浏览器打开http://localhost:3000。 你会看到下面的输出。
我们能够很清楚的看到handle的流程控制。我们嵌套他们的返回顺序。我们可以通过中间件中得 return 随时停止handle链的控制。
在上面的代码中我们在middlewareTwo function包含了retrun 语句。我们在浏览器中打开http://localhost:3000/foo,我们会看到。
- 2015/12/19 04:21:57 Executing middlewareOne
-
- 2015/12/19 04:21:57 Executing middlewareTwo
-
- 2015/12/19 04:21:57 Executing middlewareOne again
-
- 2015/12/19 04:21:57 Executing middlewareOne
-
- 2015/12/19 04:21:57 Executing middlewareTwo
-
- 2015/12/19 04:21:57 Executing middlewareOne again
我们实现一个真实的项目的示例:
我们实现一个判断请求是不是XMl的功能,我们要实现一个中间件。用来检查的请求体的存在。检查请求体,以确保它是XML。如果其中检查失败,我希望我们的中间件输出错误信息然后终止我们的handle处理。
- package main
-
- import (
-
- "bytes"
-
- "net/http"
-
- )
-
- func enforceXMLHandler(next http.Handler) http.Handler {
-
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-
-
- if r.ContentLength == 0 {
-
- http.Error(w, http.StatusText(400), 400)
-
- return
-
- }
-
-
-
- buf := new(bytes.Buffer)
-
- buf.ReadFrom(r.Body)
-
- if http.DetectContentType(buf.Bytes()) != "text/xml; charset=utf-8" {
-
- http.Error(w, http.StatusText(415), 415)
-
- return
-
- }
-
- next.ServeHTTP(w, r)
-
- })
-
- }
-
- func main() {
-
- finalHandler := http.HandlerFunc(final)
-
- http.Handle("/", enforceXMLHandler(finalHandler))
-
- http.ListenAndServe(":3000", nil)
-
- }
-
- func final(w http.ResponseWriter, r *http.Request) {
-
- w.Write([]byte("OK"))
-
- }
为了检验我们的中间件是否实现了这个功能,我们首先创建一个XML文件。
-
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_34184561/article/details/90396719
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。