Go实战--Gorilla web toolkit使用之gorilla/handlers

2018 就这么来了。

苍老师也宣布结婚了。
黑龙江又一次火了:亚布力、雪乡宰客、林业大学学生车祸

2017年12月份,tiobe上golang语言势头稍有下降。

whatever
生命不止,继续 go go go !!!

之前介绍过:
Go实战–Gorilla web toolkit使用之gorilla/sessions(iris+sessions)
Go实战–Gorilla web toolkit使用之gorilla/context

今天,继续跟大家一起学习gorilla/handlers,但是关于golang中http中间件的问题,我们之后会专门详细的介绍。
这里写图片描述

gorilla/handlers

网站:
http://www.gorillatoolkit.org/pkg/handlers

获取:
go get github.com/gorilla/handlers

简介:
Package handlers is a collection of handlers (aka “HTTP middleware”) for use with Go’s net/http package (or any framework supporting http.Handler).

The package includes handlers for logging in standardised formats, compressing HTTP responses, validating content types and other useful tools for manipulating requests and responses.

API:
这里写图片描述

LoggingHandler
for logging HTTP requests in the Apache Common Log Format.

CombinedLoggingHandler
for logging HTTP requests in the Apache Combined Log Format commonly used by both Apache and nginx.

CompressHandler
for gzipping responses.

ContentTypeHandler
for validating requests against a list of accepted content types.

MethodHandler
for matching HTTP methods against handlers in a map[string]http.Handler

ProxyHeaders
for populating r.RemoteAddr and r.URL.Scheme based on the X-Forwarded-For, X-Real-IP, X-Forwarded-Proto and RFC7239 Forwarded headers when running a Go server behind a HTTP reverse proxy.

CanonicalHost
for re-directing to the preferred host when handling multiple domains (i.e. multiple CNAME aliases).

RecoveryHandler
for recovering from unexpected panics.

应用

LoggingHandler应用
从handlers.LoggingHandler函数的参数我们可以看出,它接受一个Handler,然后返回一个Handler,其实就是对现有的Handler的一次包装,这就是中间件。

import (
    "io"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
)

func main() {
    http.Handle("/", useLoggingHandler(handler()))
    http.ListenAndServe(":1234", nil)
}
func handler() http.Handler {
    return http.HandlerFunc(myHandler)
}
func myHandler(rw http.ResponseWriter, r *http.Request) {
    rw.WriteHeader(http.StatusOK)
    io.WriteString(rw, "Hello World")
}
func useLoggingHandler(next http.Handler) http.Handler {
    return handlers.LoggingHandler(os.Stdout, next)
}

访问:
curl localhost:1234
我们看日志:
::1 - - [04/Jan/2018:11:46:28 +0800] “GET / HTTP/1.1” 200 11

CombinedLoggingHandler应用
日志格式,这种日志格式输出的日志信息更详细,更全面,比如包含UA等信息,这种格式被称为Apache Combined Log Format。
CombinedLoggingHandler就是为我们提供输出一种这种格式的中间件,使用方式和LoggingHandler一样。

import (
    "io"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
)

func main() {
    http.Handle("/", useLoggingHandler(handler()))
    http.ListenAndServe(":1234", nil)
}
func handler() http.Handler {
    return http.HandlerFunc(myHandler)
}
func myHandler(rw http.ResponseWriter, r *http.Request) {
    rw.WriteHeader(http.StatusOK)
    io.WriteString(rw, "Hello World")
}
func useLoggingHandler(next http.Handler) http.Handler {
    return handlers.CombinedLoggingHandler(os.Stdout, next)
}

访问:
curl localhost:1234
我们看日志:
::1 - - [04/Jan/2018:13:44:05 +0800] “GET / HTTP/1.1” 200 11 “” “curl/7.51.0”

CompressHandler应用
这是一个压缩response的中间件,支持gzip和deflate压缩。
如果客户端的请求头里包含Accept-Encoding,并且值为gzip或者deflate,该中间件就会压缩返回的response,这样就可以减少response的大小,减少响应的时间。

package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
    r.HandleFunc("/", HomeHandler)
    gzip := handlers.CompressHandler(r)
    http.ListenAndServe(":1234", gzip)
}

func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    w.WriteHeader(http.StatusNotFound)
    fmt.Fprintf(w, "404 - not found")
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

这里写图片描述

CanonicalHost应用

gorilla := "http://www.gorillatoolkit.org"

    rr := httptest.NewRecorder()
    r := newRequest("GET", "http://www.example.com/")

    testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

    // Test a re-direct: should return a 302 Found.
    CanonicalHost(gorilla, http.StatusFound)(testHandler).ServeHTTP(rr, r)

    if rr.Code != http.StatusFound {
        t.Fatalf("bad status: got %v want %v", rr.Code, http.StatusFound)
    }

    if rr.Header().Get("Location") != gorilla+r.URL.Path {
        t.Fatalf("bad re-direct: got %q want %q", rr.Header().Get("Location"), gorilla+r.URL.Path)
    }

Allowing origins using gorilla handlers

package main

import (
    "log"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()

    log.Fatal(http.ListenAndServe(":8080",
        handlers.LoggingHandler(os.Stdout, handlers.CORS(
            handlers.AllowedMethods([]string{"POST"}),
            handlers.AllowedOrigins([]string{"*"}),
            handlers.AllowedHeaders([]string{"X-Requested-With"}))(router))))
}

命令行:
curl -H “Origin: http://example.com” -H “Access-Control-Request-Method: POST” -H “Access-Control-Request-Headers: X-Requested-With” -X OPTIONS –verbose localhost:8080

* STATE: INIT => CONNECT handle 0x2004a698; line 1407 (connection #-5000)
* Rebuilt URL to: localhost:8080/
* Added connection 0. The cache now contains 1 members
* Trying ::1...
* TCP_NODELAY set
* STATE: CONNECT => WAITCONNECT handle 0x2004a698; line 1460 (connection #0)
* Connected to localhost (::1) port 8080 (#0)
* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x2004a698; line 1567 (connection #0)
* Marked for [keep alive]: HTTP default
* STATE: SENDPROTOCONNECT => DO handle 0x2004a698; line 1585 (connection #0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With
> * STATE: DO => DO_DONE handle 0x2004a698; line 1664 (connection #0)
* STATE: DO_DONE => WAITPERFORM handle 0x2004a698; line 1791 (connection #0)
* STATE: WAITPERFORM => PERFORM handle 0x2004a698; line 1801 (connection #0)
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK < Access-Control-Allow-Headers: X-Requested-With < Access-Control-Allow-Origin: http://example.com < Date: Thu, 04 Jan 2018 05:47:31 GMT < Content-Length: 0 < Content-Type: text/plain; charset=utf-8 < * STATE: PERFORM => DONE handle 0x2004a698; line 1965 (connection #0)
* multi_done
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact
* Expire cleared

中间件写入日志文件

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"

    "github.com/gorilla/handlers"
)

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 message Handler")
    fmt.Fprintf(w, "Message Go!")
}

func main() {
    indexHandler := http.HandlerFunc(index)
    aboutHandler := http.HandlerFunc(about)

    logFile, err := os.OpenFile("server.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }

    http.Handle("/", handlers.LoggingHandler(logFile, handlers.CompressHandler(indexHandler)))
    http.Handle("about", handlers.LoggingHandler(logFile, handlers.CompressHandler(aboutHandler)))
    server := &http.Server{
        Addr: ":8080",
    }
    log.Println("Listening...")
    server.ListenAndServe()
}

这里写图片描述

相关文章
相关标签/搜索