HTTP 中间件
HTTP中间件,在现实项目开发中简化了很多工作,Go开发社区还没有很大的兴趣吸收成熟的web开发框架.而是更加愿意使用标准库的基础模块,再搭配一下第三方库比如Gorilla mux. 使用中间件可以实现很多共同的操作,比如安全验证,HTTP请求,登录响应,压缩响应头,缓存等,而且这些中间件可以应用于很多应用或者跨应用的handlers.
当开发web应用,可能需要共享一些或者全部的请求的handler操作.比如:想打印全部的请求,每个请求都进行登录逻辑的验证.通常情况下可能用到很特别的方式去解决这样的需求.使用HTTP middleware,可以很好的实现这样的功共享能.
middleware是一段可插入式,独立的代码片段.可以用于在HTTP请求的handlers的共享操作.middleware组件可以插入程序和请求handler的其他层共同协作,在HTTP handler前或者后都可以.比如,为了打印全部的网络请求,可以写一个打印的中间件,可以插入到每个handler中.并不会影响原来的功能模块,因为这是一段独立的代码片段.可以随时添加或者移除.
一些常用中间件的例子
- 打印HTTP请求和相应
- 压缩HTTP响应
- 写共同的响应头
- 创建数据库的session
- 授权验证和安全验证
写一个简单的HTTP中间件
Go的net/http包 提供了StripPrefix和TimeoutHandler,这样的函数,和中间件非常的类似:包裹了请求的handlers和提供了额外的实现.StripPrefix和TimeoutHandler都需要一个http.Handler作为参数,和其他的参数一起,最后返回一个http.Handler,这样很容易的将一些平常的操作和逻辑包裹进来.StripPrefix需要一个string参数和一个http.Handler.返回一个hander,过滤掉URL中的没有string参数的请求.
// middleware
package main
import (
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("public"))
http.Handle("/public/", http.StripPrefix("/public/", fs))
http.ListenAndServe(":9090", nil)
}
/*如果我们在浏览器输入:localhost:9090是无法访问的,输入:localhost:9090/public/才能看到对应的文件夹内容*/
// middleware
package main
import (
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("public"))
http.ListenAndServe(":9090", fs)
}
/*如果我们在浏览器输入:localhost:9090就能直接访问*/
StripPrefix函数包裹了http.FileServer的handler,并提供了另外的操作,执行给定的handler对象.
写一个HTTP Middleware的步骤
1.写一个方法,参数其一是一个http.Handler,会在中间件内调用这个参数的ServeHTTP方法.
2.返回一个handler.
middleware的模板
func middlewareHandler(next http.Handler) http.Handler{
returen http.HandlerFunc(func(w http.ResponseWriter,r *http.Request){
//中间件的逻辑在这里实现,在执行传递进来的handler之前
next.ServeHTTP(w,r)
//在handler执行之后的中间件逻辑
})
}
- 实现一个登陆的中间件
// middleware
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func logginHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("Started %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
})
}
func index(w http.ResponseWriter, r *http.Request) {
log.Println("Execute index handler")
fmt.Fprintf(w, "Welcome!")
}
func about(w http.ResponseWriter, r *http.Request) {
log.Println("Execute about handler")
fmt.Fprintf(w, "About,Go Lang!")
}
func icoHandler(w http.ResponseWriter, r *http.Request) {
}
func main() {
http.HandleFunc("/favicon.icon", icoHandler)
indexHandler := http.HandlerFunc(index)
aboutHandler := http.HandlerFunc(about)
http.Handle("/", logginHandler(indexHandler))
http.Handle("/about", logginHandler(aboutHandler))
server := &http.Server{
Addr: ":9090",
}
log.Println("Listening...")
server.ListenAndServe()
}
//访问相应的路径,控制台输出类似结果
/*
2016/07/29 08:41:27 Listening...
2016/07/29 08:41:52 Started GET /
2016/07/29 08:41:52 Execute index handler
2016/07/29 08:41:52 Completed / in 33.676µs
2016/07/29 08:42:24 Started GET /about
2016/07/29 08:42:24 Execute about handler
2016/07/29 08:42:24 Completed /about in 22.953µs
*/
在这个例子,logginHandler作为了一个HTTP中间件,装置在"/"和"/about"的路由,中间件可以实现在多个handler共享操作.在logginHandler中间件,登陆信息在传递进来的handler前后都打印了登陆的方法和URL路径,还有执行的时间.执行传递进来的handler是在这一句代码:next.ServeHTTP(w,r)
- 控制中间件的流程
HTTP中间件需要一个http.Handler作为参数,并且返回一个http.Handler,所以很轻松的把中间件的handler链接起来,最后调用传递进来的handler.理解中间件的handler的流程非常重要,特别是把他们链接起来的时候.
例子
// middleware
package main
import (
"fmt"
"log"
"net/http"
)
func middlewareFirst(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("MiddlewareFirst - Before Handler")
next.ServeHTTP(w, r)
log.Println("MiddlerwareFirst - After Handler")
})
}
func middlerwareSecond(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("MiddlerwareSecnd - Before Handler")
if r.URL.Path == "/message" {
if r.URL.Query().Get("password") == "123456" {
log.Println("Authorized to system")
next.ServeHTTP(w, r)
} else {
log.Println("Failed to authorize to the system")
}
} else {
next.ServeHTTP(w, r)
}
log.Println("Middleware - After Handler")
})
}
func index(w http.ResponseWriter, r *http.Request) {
log.Println("Execute index Handler")
fmt.Fprintf(w, "Welcome!")
}
func message(w http.ResponseWriter, r *http.Request) {
log.Println("Execute message Handler")
fmt.Fprintf(w, "Message Go!")
}
func main() {
http.Handle("/", middlewareFirst(middlerwareSecond(http.HandlerFunc(index))))
http.Handle("/message", middlewareFirst(middlerwareSecond(http.HandlerFunc(message))))
server := &http.Server{
Addr: ":9090",
}
log.Println("Listeing...")
server.ListenAndServe()
}
/*
访问相应的路径,控制台打印:
2016/07/29 09:14:15 Listeing...
2016/07/29 09:14:21 MiddlewareFirst - Before Handler
2016/07/29 09:14:21 MiddlerwareSecnd - Before Handler
2016/07/29 09:14:21 Failed to authorize to the system
2016/07/29 09:14:21 Middleware - After Handler
2016/07/29 09:14:21 MiddlerwareFirst - After Handler
2016/07/29 09:14:26 MiddlewareFirst - Before Handler
2016/07/29 09:14:26 MiddlerwareSecnd - Before Handler
2016/07/29 09:14:26 Execute index Handler
2016/07/29 09:14:26 Middleware - After Handler
2016/07/29 09:14:26 MiddlerwareFirst - After Handler
*/
观察打印信息可以很好的理解中间件的handler的流程,在这里MiddlewareFirst和middlewareSecond还handler内调用,可以将此应用到程序的handler.在middlewareSecond方法,会验证密码.上面的流程如下:
控制流先到:middlewareFirst函数.
在打印消息之后,调用next.ServeHTTP方法,进入到MiddlewareSecond方法.
在middlewareSecond方法执行打印信息之后,next.ServeHTTP方法被调用.
(1)如果路径是"/",会调用index handler,不需要验证授权.
(2)如果路径是"/message",就会验证参数的密码,控制流会返回到middlewareFirst方法.如果验证通过,就会执行传递进来的handler如果执行了传递进来的handler(验证通过),就会执行next.ServeHTTP方法
最后还回到middlewareFirst