Golang Web Development (2)

8. Middleware (Basic)

This example will show how to create basic logging middleware in Go.
The middleware simply accepts a http.HandlerFuncas one of its parameters, wraps it and returns a new one http.HandlerFuncto the server call.

// basic-middleware.go
package main

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

func logging(f http.HandlerFunc) http.HandlerFunc {
    
    
    return func(w http.ResponseWriter, r *http.Request) {
    
    
        log.Println(r.URL.Path)
        f(w, r)
    }
}

func foo(w http.ResponseWriter, r *http.Request) {
    
    
    fmt.Fprintln(w, "foo")
}

func bar(w http.ResponseWriter, r *http.Request) {
    
    
    fmt.Fprintln(w, "bar")
}

func main() {
    
    
    http.HandleFunc("/foo", logging(foo))
    http.HandleFunc("/bar", logging(bar))

    http.ListenAndServe(":8080", nil)
}
$ go run basic-middleware.go
2017/02/10 23:59:34 /foo
2017/02/10 23:59:35 /bar
2017/02/10 23:59:36 /foo?bar

$ curl -s http://localhost:8080/foo
$ curl -s http://localhost:8080/bar
$ curl -s http://localhost:8080/foo?bar

9. Middleware (Advanced)

This example will show how to create a more advanced version of middleware in Go.

The middleware itself just takes one http.HandlerFuncas one of its parameters, wraps it and returns a new one http.HandlerFuncto the server call.

Here we define a new type Middlewarethat makes it easier to chain multiple middleware together . The idea was inspired by a talk by Mat Ryers on building APIs. You can find a more detailed explanation including the talk here.

This code explains in detail how to create new middleware. In the full example below, we've simplified this version with some boilerplate code.

func createNewMiddleware() Middleware {
    
    

    // Create a new Middleware
    middleware := func(next http.HandlerFunc) http.HandlerFunc {
    
    

        // Define the http.HandlerFunc which is called by the server eventually
        handler := func(w http.ResponseWriter, r *http.Request) {
    
    

            // ... do middleware things

            // Call the next middleware/handler in chain
            next(w, r)
        }

        // Return newly created handler
        return handler
    }

    // Return newly created middleware
    return middleware
}

Here's the full example:

// advanced-middleware.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

type Middleware func(http.HandlerFunc) http.HandlerFunc

// Logging logs all requests with its path and the time it took to process
func Logging() Middleware {
    
    

    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {
    
    

        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
    
    

            // Do middleware things
            start := time.Now()
            defer func() {
    
     log.Println(r.URL.Path, time.Since(start)) }()

            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}

// Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
func Method(m string) Middleware {
    
    

    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {
    
    

        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
    
    

            // Do middleware things
            if r.Method != m {
    
    
                http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
                return
            }

            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}

// Chain applies middlewares to a http.HandlerFunc
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    
    
    for _, m := range middlewares {
    
    
        f = m(f)
    }
    return f
}

func Hello(w http.ResponseWriter, r *http.Request) {
    
    
    fmt.Fprintln(w, "hello world")
}

func main() {
    
    
    http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
    http.ListenAndServe(":8080", nil)
}

10. Sessions

This example will show how to store data in a session cookie using the popular gorilla/sessions package in Go.

Cookies are small pieces of data stored in the user's browser and sent to our server with each request . In them we can store, for example, whether a user is logged in to our website, and find out who he actually is (in our system).

In this example, we will only allow authenticated users /secretto view our secret message on the page. To access it, the user must first visit /loginto obtain a valid session cookie, thereby logging in. Additionally, he has access to /logoutrevoke his access to our secret messages.

// sessions.go
package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/sessions"
)

var (
    // It is recommended to use an authentication key with 32 or 64 bytes.
	// The encryption key, if set, must be either 16, 24, or 32 bytes to select
	// AES-128, AES-192, or AES-256 modes.
    key = []byte("super-secret-key")
    store = sessions.NewCookieStore(key)
)

func secret(w http.ResponseWriter, r *http.Request) {
    
    
    session, _ := store.Get(r, "cookie-name")

    // Check if user is authenticated
    if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
    
    
        http.Error(w, "Forbidden", http.StatusForbidden)
        return
    }

    // Print secret message
    fmt.Fprintln(w, "The cake is a lie!")
}

func login(w http.ResponseWriter, r *http.Request) {
    
    
	// Get 在将给定名称的会话添加到注册中心后返回给定名称的会话。
	// 如果会话不存在,则返回一个新会话。访问会话上的IsNew,以检查它是现有会话还是新会话。
	// 它返回一个新的会话,如果会话存在但无法解码,则返回一个错误。
    session, _ := store.Get(r, "cookie-name")

    // Authentication goes here
    // ...

    // Set user as authenticated
    session.Values["authenticated"] = true
    session.Save(r, w)
}

func logout(w http.ResponseWriter, r *http.Request) {
    
    
    session, _ := store.Get(r, "cookie-name")

    // Revoke users authentication
    session.Values["authenticated"] = false
    session.Save(r, w)
}

func main() {
    
    
    http.HandleFunc("/secret", secret)
    http.HandleFunc("/login", login)
    http.HandleFunc("/logout", logout)

    http.ListenAndServe(":8080", nil)
}
$ go run sessions.go

$ curl -s http://localhost:8080/secret
Forbidden

$ curl -s -I http://localhost:8080/login
Set-Cookie: cookie-name=MTQ4NzE5Mz...

$ curl -s --cookie "cookie-name=MTQ4NzE5Mz..." http://localhost:8080/secret
The cake is a lie!

11、JSON

This example will show how to encode and decode JSON data using the encoding/json package

// json.go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type User struct {
    
    
    Firstname string `json:"firstname"`
    Lastname  string `json:"lastname"`
    Age       int    `json:"age"`
}

func main() {
    
    
    http.HandleFunc("/decode", func(w http.ResponseWriter, r *http.Request) {
    
    
        var user User
        json.NewDecoder(r.Body).Decode(&user)

        fmt.Fprintf(w, "%s %s is %d years old!", user.Firstname, user.Lastname, user.Age)
    })

    http.HandleFunc("/encode", func(w http.ResponseWriter, r *http.Request) {
    
    
        peter := User{
    
    
            Firstname: "John",
            Lastname:  "Doe",
            Age:       25,
        }

        json.NewEncoder(w).Encode(peter)
    })

    http.ListenAndServe(":8080", nil)
}
$ go run json.go

$ curl -s -XPOST -d'{"firstname":"Elon","lastname":"Musk","age":48}' http://localhost:8080/decode
Elon Musk is 48 years old!

$ curl -s http://localhost:8080/encode
{
    
    "firstname":"John","lastname":"Doe","age":25}

12、Websockets

This example will show how to use websockets in Go . We'll build a simple server that will respond to everything we send it. For this we have to go getthe popular gorilla/websocket library as follows:

$ go get github.com/gorilla/websocket

From now on, every application we write can use this library.

// websockets.go
package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    
    
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
}

func main() {
    
    
    http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) {
    
    
        conn, _ := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity

        for {
    
    
            // Read message from browser
            msgType, msg, err := conn.ReadMessage()
            if err != nil {
    
    
                return
            }

            // Print the message to the console
            fmt.Printf("%s sent: %s\n", conn.RemoteAddr(), string(msg))

            // Write message back to browser
            if err = conn.WriteMessage(msgType, msg); err != nil {
    
    
                return
            }
        }
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    
    
        http.ServeFile(w, r, "websockets.html")
    })

    http.ListenAndServe(":8080", nil)
}
<!-- websockets.html -->
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
<script>
    var input = document.getElementById("input");
    var output = document.getElementById("output");
    var socket = new WebSocket("ws://localhost:8080/echo");

    socket.onopen = function () {
      
      
        output.innerHTML += "Status: Connected\n";
    };

    socket.onmessage = function (e) {
      
      
        output.innerHTML += "Server: " + e.data + "\n";
    };

    function send() {
      
      
        socket.send(input.value);
        input.value = "";
    }
</script>

13、Password Hashing (bcrypt)

This example will show how to bcrypthash a password using . go getFor this we have to golang bcryptlibrary like this :

$ go get golang.org/x/crypto/bcrypt

From now on, every application we write can use this library.

// passwords.go
package main

import (
    "fmt"

    "golang.org/x/crypto/bcrypt"
)

func HashPassword(password string) (string, error) {
    
    
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
    
    
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

func main() {
    
    
    password := "secret"
    hash, _ := HashPassword(password) // ignore error for the sake of simplicity

    fmt.Println("Password:", password)
    fmt.Println("Hash:    ", hash)

    match := CheckPasswordHash(password, hash)
    fmt.Println("Match:   ", match)
}
$ go run passwords.go
Password:  secret
Hash:  $2a$14$7LxzgCK.u0regxQiUyQc6.701P8FWCFvbEacAD8eP6Xw49Jq2sKuy
Match:  true

Guess you like

Origin blog.csdn.net/chinusyan/article/details/130196022