创建一个http服务
先创建一个基础的http服务,代码如下:
package mainimport ("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 mainimport ("log""net/http")func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte("Hello World!\n"))})// http默认使用的http.DefaultServeMux,如果使用自定义的handler,这里改成自定义handlerhandler := TestMiddleware(http.DefaultServeMux)// 这里需要修改为添加了中间件之后的handlerhttp.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 mainimport ("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 mainimport ("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内容重新赋值给bodyr.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 自定义的一个ResponseWritertype CustomResponseWriter struct {http.ResponseWriter}func (w *CustomResponseWriter) Write(b []byte) (int, error) {// 接管body的写入,在这里可以修改响应的内容// 在Hello World!\n后面添加一个 smileb = 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响应码固定成200w.ResponseWriter.WriteHeader(http.StatusOK)}
现在将状态码固定成了200,修改handler中的内容,写入500的响应码
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.WriteHeader(http.StatusInternalServerError)})



