这里说的中间件是指在接收到用户消息,先进行一系列的预处理,再交给实际的http.Handler去处理。
beego通过修饰器的方式实现pipeline的中间件。
// MiddleWare function for http.Handler
type MiddleWare func(http.Handler) http.Handler
// Run beego application.
func (app *App) Run(mws ...MiddleWare) {
......
app.Server.Handler = app.Handlers
for i := len(mws) - 1; i >= 0; i-- {
if mws[i] == nil {
continue
}
app.Server.Handler = mws[i](app.Server.Handler)
}
......
}
修饰器是一种常见的设计模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式比生成子类更为灵活,可以给某个对象而不是整个类添加一些功能。
原理是增加一个修饰类包裹原来的类,修饰类可以添加一些新的功能,不需要用到新功能的地方,直接调用原来类中的方法。
go中没有类的概念,通过结构体成员嵌套直接可以使用成员的方法,并用该结构体实现新的方法。
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}
// 对AcceptTCP()返回的连接,进行一些设置,再返回修改后的连接。
func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
tc, err := ln.AcceptTCP()
if err != nil {
return nil, err
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
// AcceptTCP accepts the next incoming call and returns the new
// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
if !l.ok() {
return nil, syscall.EINVAL
}
c, err := l.accept()
if err != nil {
return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
}
return c, nil
}
在go中,函数是第一类的,所以也可以包裹函数,实现对函数的修饰。修饰器是在运行时动态增加对象的行为,在执行包裹对象的前后执行特定的逻辑。
//服务器统一出错处理。对实际执行业务逻辑的handler函数进行包裹,统一处理返回的err。
type Handler func(http.ResponseWriter, *http.Request) error
func ErrWrapper(handler Handler) func(http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
err := handler(writer, request)
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}