如何编写Go语言中间件的实例教程
作者:HULK一线技术杂谈 发布时间:2024-04-25 15:29:05
引言
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去编写一个中间件。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对asp之家的支持。
来源:https://mp.weixin.qq.com/s/-nRWwy8SjW1TlqCglL0CAQ


猜你喜欢
- bootstrap 中的bootstrapValidator可以对前端的数据进行验证,但是有的时候我们需要动态的添加验证,这样需要我们动态的
- 自己做了一个asp页面,用了一个td来显示留言本的最新消息;但是却无法控制其排版问题,因为该td的width确定,但是数据库中的文字却是未知
- 前言:牛奶冻曲线(blancmange curve),因在1901年由高木贞治所研究,又称高木曲线。在单位区间内,牛奶冻函数定义为:分形曲线
- Python使用for实现无限循环# 方法1.1:借助循环遍历列表的cycle方法from itertools import cyclefo
- 本文实例为大家分享了Python基于Socket实现简单聊天室,供大家参考,具体内容如下服务端#!/usr/bin/env python#
- 首先给大家分享一篇js中int和string数据类型互相转化实例https://www.aspxhome.com/article/15474
- 前言之前我们分享过用Python进行可视化的9种常见方式。其实我们还能让可视化图形逼格更高一些,今天就分享一下如何让可视化秀起来:用Pyth
- 利用python进行求解,求解的要求是不能使用python内部封装好的函数例如:maxway1:def findmax(data,n): i
- 本文实例讲述了Python实现的使用telnet登陆聊天室。分享给大家供大家参考。具体如下:前久在家学习Python的时候写的一个简单的聊天
- from http://www.devshed.com/c/a/MySQL/Error-Handling-Examples/ Error H
- 1433 SQL入侵恢复xp_cmdshell方法总结 sql server 2005下开启xp_cmdshell的办法 EXEC sp_c
- 触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程。触发器主要是通过事件进行触发被自动调用执行的。而存储过程可以通过存储过程的
- Python实现模拟时钟代码推荐# coding=utf8import sys, pygame, math, randomfrom pyga
- 【添加事件机制】 addEventListener 和 attachEvent[W3C]addEvent
- 现像如下:站点无法打开,或者打开很慢.HTML可以打开.重新启动或者回收应用程序池可恢复.但过一段时间又会出现日志里会有:ISAPI
- 在tensorflow中,我们可以使用 tf.device() 指定模型运行的具体设备,可以指定运行在GPU还是CUP上,以及哪块GPU上。
- 来源:在工作过程中,需要统计一些trace信息,也就是一些打点信息,而打点是通过关键字进行的,因此对一个很大的文件进行分析时,想把两个打点之
- 平时我们获取事件对象一般写法如下:function getEvent(event) { return event
- 百度AI提供了一天50000次的免费文字识别额度,可以愉快的免费使用!下面直接上方法:首先在百度AI创建一个应用,按照下图创建即可,创建后会
- 本文实例分析了Go语言多值替换的HTML模板用法。分享给大家供大家参考。具体如下:这里通过两种方式提供基于HTML模板的多变量值替换。另外附