如何编写Go语言中间件的实例教程

引言

web开发的背景下,“中间件”通常意思是“包装原始应用并添加一些额外的功能的应用的一部分”。这个概念似乎总是不被人理解,但是我认为中间件非常棒。

首先,一个好的中间件有一个责任就是可插拔并且自足。这就意味着你可以在接口级别嵌入你的中间件他就能直接运行。它不会影响你编码方式,不是框架,仅仅是你请求处理里面的一层而已。完全没必要重写你的代码,如果你想使用中间件的一个功能,你就帮他插入到那里,如果不想使用了,就可以直接移除。

纵观Go语言,中间件是非常普遍的,即使在标准库中。虽然开始的时候不会那么明显,在标准库net/http中的函数StripText或者TimeoutHandler就是我们要定义和的中间件的样子,处理请求和相应的时候他们包装你的handler,并处理一些额外的步骤。

一开始,我们认为编写中间件似乎很容易,但是我们实际编写的时候也会遇到各种各样的坑。让我们来看看一些例子。

1、读取请求

在我们的示例中,所有的中间件都将接受http。处理程序作为一个参数,并返回一个http.Handler。这使得人们很容易就能把中间产品串起来。我们所有的中间产品的基本模式是这样的:

func X(h http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 // Something here...
 h.ServeHTTP(w, r)
 })
}

我们想要将所有的请求重定向到一个斜杠——比方说/message/,到它们的非斜杠等效,比如/message。我们可以这样写:

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文档中指明:

除了读取主体之外,处理程序不应该修改所提供的请求。

Go标准库复制http.Request。请求对象在将其传递到响应链之前,我们也应该这样做。假设我们想在每个请求上设置Request-Id头,以便内部跟踪。创建*Request的浅副本,并在代理之前修改标题。

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、编写响应头

如果你想设置响应头,你可以只写它们,然后代理请求。

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,但是如果用户试图写一个响应,我们就会潜入并添加我们的标题。

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)
}

要在我们的中间件中使用它,我们会写:

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调用之后再添加校验。

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支持,这是不好的。

// 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去编写一个中间件。

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

(0)

相关推荐

  • 如何编写Go语言中间件的实例教程

    引言 web开发的背景下,"中间件"通常意思是"包装原始应用并添加一些额外的功能的应用的一部分".这个概念似乎总是不被人理解,但是我认为中间件非常棒. 首先,一个好的中间件有一个责任就是可插拔并且自足.这就意味着你可以在接口级别嵌入你的中间件他就能直接运行.它不会影响你编码方式,不是框架,仅仅是你请求处理里面的一层而已.完全没必要重写你的代码,如果你想使用中间件的一个功能,你就帮他插入到那里,如果不想使用了,就可以直接移除. 纵观Go语言,中间件是非常普遍的,即使在

  • VS Code 中安装运行、编写C语言程序的详细教程

    目录 1.下载 安装VS Code 2.安装VS code中2个插件 3.下载mingb64 4.配置系统的环境变量 5.C语言配置 6.编写一个测试程序 1.下载 安装VS Code 去官网下载:https://code.visualstudio.com/Download 直接下载,安装即可. 2.安装VS code中2个插件 打开软件运行,在扩展商店中分别搜索安装 C/C++ 和 code runner 插件 3.下载mingb64 官网下载 https://sourceforge.net/

  • python3编写C/S网络程序实例教程

    本文以实例形式讲述了python3编写C/S网络程序的实现方法.具体方法如下: 本文所述实例是根据wingIDE的提示编写的一个C/S小程序,具体代码如下: client端myclient.py代码如下: #!/bin/env python #-*- coding:gb18030 -*- # import socket import time i=1 while i<10: address=("127.0.0.1",3138) s=socket.socket(socket.AF_

  • C++编写高性能服务器实例教程

    我将展示如何使用现代C++编写一个Echo服务器,相当于分布式系统开发中的"Hello World".这个服务器会将接收的消息直接返回.我们同时需要一个可以向我们的服务器发动消息的客户端,在这里可以发现客户端的源码. Wangle是一个用来搭建事件驱动的现代异步C++服务的C/S应用框架.Wangle最基本的抽象概念就是Pipeline(管线).能够理解这种抽象,将会很容易写出各种复杂的现代C++服务,另一个重要的概念是Service(服务),其可以看作一种更高级的Pipeline,不

  • 详解Dev C++使用教程(使用Dev C++编写C语言程序)

    前面我们给出了一段完整的C语言代码,就是在显示器上输出"C语言中文网",如下所示: #include <stdio.h> int main() { puts("C语言中文网"); return 0; } 本节我们就来看看如何通过 Dev C++ 来运行这段代码. Dev C++ 支持单个源文件的编译,如果你的程序只有一个源文件(初学者基本都是在单个源文件下编写代码),那么不用创建项目,直接运行就可以:如果有多个源文件,才需要创建项目. 1) 新建源文件

  • 使用emacs编写C语言教程

    如何使用emacs编写c语言程序,并编译运行 vi和emacs被分别被称为编辑器之神和神之编辑器.vi的入门精通都很难,emacs入门容易,精通难:vi使用起来不停地切换模式,而emacs则不停地ctrl,meta等组合键.因此,高德纳大师说操作Emacs,就像弹奏管风琴. vi是有模式的,因此他很少使用组合键,编辑起来确实要高效,而我厌烦不停的模式切换.选择了emacs.很巧,在国外,几乎所有的大师都是使用emacs的,James Gosling们(Java语言设计者),Donald Knut

  • Go 语言选择器实例教程

    目录 引言 选择器的深度 有效选择器 唯一性+最浅深度 空指针 接口 一个特殊情况 在 hood 下 引言 在 Go 语言中,表达式 foo.bar 可能表示两件事.如果 foo 是一个包名,那么表达式就是一个所谓的限定标识符,用来引用包 foo 中的导出的标识符.由于它只用来处理导出的标识符,bar 必须以大写字母开头(译注:如果首字母大写,则可以被其他的包访问:如果首字母小写,则只能在本包中使用): package foo import "fmt" func Foo() { fmt

  • C语言杨氏矩阵实例教你编写

    目录 一.杨氏矩阵是什么 二.编写步骤 三.程序的改进 四.总结 一.杨氏矩阵是什么 一个数字矩阵,矩阵的每一行从左到右一次递增,矩阵从上到下递增,在这样的矩阵中查找一个数字是否存在.时间复杂度小于O(N). 二.编写步骤 1.首先给出一个三乘三的矩阵,我们想找到数字7是否在这个数组之中,如图所示. 2. 我们给出的方法是一种从右上角开始,另一种是从左下角开始.比如从右上角开始,编写一个函数. 3. 函数的编写.从右上角开始,将右上角数字与数字7比较,如果数字7大于右上角的数字(3)(因为第一行

  • ReactJS入门实例教程详解

    目录 一.ReactJS简介 二.对ReactJS的认识及ReactJS的优点 1.ReactJS的背景和原理 2.组件化 三.下载ReactJS,编写Hello,world 四.Jsx语法 五.ReactJS组件 1.组件属性 2.组件状态 3.组件的生命周期 4.组件的嵌套 六.ReactJS小结 七.参考资料 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我

  • web标准布局实例教程,用定位轻松解决CSS复杂布局

    我相信来经典论坛学习标准的朋友,99%都不是为了进W3C,不是为了成为专业的研究人员,那么大家来的目的是什么呢?很简单,其实都是想通过标准的学习让自己多增加一项技能而已,让自己在找工作的时候不会在被"需要熟练掌握web标准"而难倒,在下面的实例中xhtml并没有什么语意,文章的目的只是让然大家能更好的掌握CSS中相对.绝对定位的用法(新手问此问题的确实很多),及如何有效的结合背景,实现比较复杂的布局.不足之处还望前辈们能指点一二,感谢! ------------------------

随机推荐