创建一个http服务
先创建一个基础的http服务,代码如下:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!\n"))
})
http.ListenAndServe(":8080", nil)
}
如何创建一个中间件
中间件的定义形式为
func (http.Handler) http.Hander
传入一个**http.Handler**
返回一个 **http.Hanlder**
。
来创建一个中间件
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("use middleware")
next.ServeHTTP(w, r)
})
}
修改服务代码,使其支持中间件
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!\n"))
})
// http默认使用的http.DefaultServeMux,如果使用自定义的handler,这里改成自定义handler
handler := TestMiddleware(http.DefaultServeMux)
// 这里需要修改为添加了中间件之后的handler
http.ListenAndServe(":8080", handler)
}
访问服务,测试是否使用了中间件
请求的时候,触发了中间件日志。
如何在中间件中访问或操作Request
中间件为洋葱模型,所以在操作Request时,需要在next.ServeHTTP
调用之前进行操作。
1. 如何获取请求头信息
获取请求头信息可以直接通过header来获取,修改中间件,获取请求头信息
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("use middleware")
// 获取请求头信息
header := r.Header.Values("Accept")
log.Println("Accept:", header)
next.ServeHTTP(w, r)
})
}
2. 如何添加或修改请求头信息
package main
import (
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 在代码中获取请求头
log.Println("Code Get Accept:", r.Header.Values("Accept"))
w.Write([]byte("Hello World!\n"))
})
handler := TestMiddleware(http.DefaultServeMux)
http.ListenAndServe(":8080", handler)
}
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("use middleware")
// 在中间件中获取请求头信息
header := r.Header.Values("Accept")
log.Println("Middleware GET Accept:", header)
// 在中间件中设置请求头
r.Header.Set("Accept", "application/json")
next.ServeHTTP(w, r)
})
}
可以看出,设置请求头之后,在code处获取的请求头信息发生了变化。
3. 如何获取请求body
示例一: 直接使用Request.Boby
package main
import (
"bytes"
"io"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
b := new(bytes.Buffer)
io.Copy(b, r.Body)
log.Println("Code Get Body:", b.String())
w.Write([]byte("Hello World!\n"))
})
handler := TestMiddleware(http.DefaultServeMux)
http.ListenAndServe(":8080", handler)
}
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("use middleware")
b := new(bytes.Buffer)
io.Copy(b, r.Body)
defer r.Body.Close()
log.Println("Middleware Get Body: ", b.String())
next.ServeHTTP(w, r)
})
}
通过请求发现,在中间件中读入body之后,会导致后面的handler无法使用body中的数据,因为已经被读完了,后面继续读取的时候,就读取不到数据了,因此在使用完request.Body
后需要记得给body赋值。
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("use middleware")
b := new(bytes.Buffer)
io.Copy(b, r.Body)
defer r.Body.Close()
log.Println("Middleware Get Body: ", b.String())
// 将读取到的body内容重新赋值给body
r.Body = io.NopCloser(b)
next.ServeHTTP(w, r)
})
}
4. 如何修改请求body
修改body内容只需要在读取body内容后,将修改的body赋值给request.Body
即可。
如何在中间件中访问或操作Response
在上诉代码中,如果没有设置响应头,我们调用发现响应头如下:
系统默认设置了Date,Content-Length和Content-Type头信息,下面我们通过中间件修改Content-Type
0. 测试下Response在Write之后,能不能进行修改
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
// 在ServeHTTP()之后添加响应头信息
w.Header().Add("Content-Type", "application/json")
})
}
结果发现不能修改响应头信息。
阅读源码发现:
在http.response
中,当调用Write()
方法之后,会调用WriteHeader()
方法,如果调用的该方法,后面继续设置Header的时候,都只会操作 response.Header
中的内容,实际返回的头信息在response.cw.Header
中。
所以我们需要在WriteHeader()
调用前进行操作。
1. 在Response中新增头信息
修改中间件代码
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}
中间件在调用 next.ServeHTTP
之前设置响应头信息,这样在响应时才是我们设置的响应头信息
2. 修改Response中的body和statusCode
Response头信息在next.ServeHTTP()
调用前就可以进行设置,但是ResponseBody的数据,必须要在ServeHttp()
调用之后才会写入,并且ResponseWriter
也没有提供读取响应体的接口,所以这里需要采用自定义响应结构体的方法来修改Response
中的body
和statusCode
等数据。
自定义一个ResponseWriter
// CustomResponseWriter 自定义的一个ResponseWriter
type CustomResponseWriter struct {
http.ResponseWriter
}
func (w *CustomResponseWriter) Write(b []byte) (int, error) {
// 接管body的写入,在这里可以修改响应的内容
// 在Hello World!\n后面添加一个 smile
b = append(b, []byte("😁")...)
return w.ResponseWriter.Write(b)
}
修改中间件,使用自定义ResponseWriter
func TestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(&CustomResponseWriter{
ResponseWriter: w,
}, r)
})
}
查看效果
在某些非Restful接口中,经常会统一返回200的状态码,根据自带的code来判断处理结果,那么如何修改http返回的状态码呢?
如果需要修改Response中的状态码,则需要自定义WriterHeader()
方法。修改上面自定义ResponseWriter:
func (w *CustomResponseWriter) WriterHeader(statusCode int) {
// 把状态码放入自定义Code中,在需要的时候访问
w.Code = statusCode
// http响应码固定成200
w.ResponseWriter.WriteHeader(http.StatusOK)
}
现在将状态码固定成了200,修改handler中的内容,写入500的响应码
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
})