1. 程式人生 > >Go實戰--golang中使用Goji微框架(Goji+Mongodb構建微服務)

Go實戰--golang中使用Goji微框架(Goji+Mongodb構建微服務)

生命不止,繼續 go go go!!!

今天跟大家分享一個web微框架Goji.

Goji

What is Goji?

枸杞?

Goji is a HTTP request multiplexer, similar to net/http.ServeMux. It compares incoming requests to a list of registered Patterns, and dispatches to the Handler that corresponds to the first matching Pattern. Goji also supports Middleware (composable shared functionality applied to every request) and uses the standard context to store request-scoped values.

Star:
577

獲取:
go get goji.io

示例:

package main

import (
    "fmt"
    "net/http"

    "goji.io"
    "goji.io/pat"
)

func hello(w http.ResponseWriter, r *http.Request) {
    name := pat.Param(r, "name")
    fmt.Fprintf(w, "Hello, %s!", name)
}

func main() {
    mux := goji.NewMux()
    mux.HandleFunc(pat.Get("/hello/:name"
), hello) http.ListenAndServe("localhost:8000", mux) }

zenazn/goji

不要奇怪, zenazn/goji is new version of Goji,也就是一個作者。
This project has been superseded by a new version of Goji by the same author, which has very similar primitives and semantics, but has been updated to reflect several years of experience with this library and the surrounding Go ecosystem. This project is still well-loved and well-maintained, and will be for the foreseeable future, but new projects are encouraged to use goji.io instead.

Goji是一個用Go語言寫的Web微框架。Goji的設計理念是簡單、可組合性、自由。它具有以下特點:

  • 相容net/http
  • url模式(同時支援 Sinatra 風格的 /foo/:bar 和 正則表示式)
  • 可重構中介軟體棧
  • 通過中介軟體和處理器處理上下文/環境物件
  • 自動支援 Einhorn、systemd等
  • 漸進停止,配合 Einhorn 可實現零下線時間的漸進過載

Star:
3354

獲取:
go get github.com/zenazn/goji

示例:

package main

import (
    "fmt"
    "net/http"

    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
)

func hello(c web.C, w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
}

func main() {
    goji.Get("/hello/:name", hello)
    goji.Serve()
}

執行:
2017/12/20 10:37:27.132172 Starting Goji on [::]:8000
2017/12/20 10:37:36.423659 [LAPTOP-MNU6522J/9tNAt5dWFL-000001] Started GET “/hello/heyGoji” from [::1]:19489
2017/12/20 10:37:36.423659 [LAPTOP-MNU6522J/9tNAt5dWFL-000001] Returning 200 in 0s

應用

檔案服務

package main

import (
    "fmt"
    "net/http"

    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
)

func apiExampleHandler(context web.C, resp http.ResponseWriter, req *http.Request) {
    fmt.Fprint(resp, "You've hit the API!")
}

func main() {
    goji.Handle("/api", apiExampleHandler)

    // Static file handler should generally be the last handler registered. Otherwise, it'll match every path.
    // Be sure to use an absolute path.
    staticFilesLocation := "./"
    goji.Handle("/*", http.FileServer(http.Dir(staticFilesLocation)))

    goji.Serve()
}

Goji Restful Api

package main

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

    "goji.io"
    "goji.io/pat"
)

type book struct {
    ISBN    string "json:isbn"
    Title   string "json:name"
    Authors string "json:author"
    Price   string "json:price"
}

var bookStore = []book{
    book{
        ISBN:    "0321774639",
        Title:   "Programming in Go: Creating Applications for the 21st Century (Developer's Library)",
        Authors: "Mark Summerfield",
        Price:   "$34.57",
    },
    book{
        ISBN:    "0134190440",
        Title:   "The Go Programming Language",
        Authors: "Alan A. A. Donovan, Brian W. Kernighan",
        Price:   "$34.57",
    },
}

func main() {
    mux := goji.NewMux()
    mux.HandleFunc(pat.Get("/books"), allBooks)
    mux.HandleFunc(pat.Get("/books/:isbn"), bookByISBN)
    mux.Use(logging)
    http.ListenAndServe("localhost:8080", mux)
}

func allBooks(w http.ResponseWriter, r *http.Request) {
    jsonOut, _ := json.Marshal(bookStore)
    fmt.Fprintf(w, string(jsonOut))
}

func bookByISBN(w http.ResponseWriter, r *http.Request) {
    isbn := pat.Param(r, "isbn")
    for _, b := range bookStore {
        if b.ISBN == isbn {
            jsonOut, _ := json.Marshal(b)
            fmt.Fprintf(w, string(jsonOut))
            return
        }
    }
    w.WriteHeader(http.StatusNotFound)
}

func logging(h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        fmt.Printf("Received request: %v\n", r.URL)
        h.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

返回:

// 20171220105345
// http://localhost:8080/books

Array[2][
  {
    "ISBN": "0321774639",
    "Title": "Programming in Go: Creating Applications for the 21st Century (Developer's Library)",
    "Authors": "Mark Summerfield",
    "Price": "$34.57"
  },
  {
    "ISBN": "0134190440",
    "Title": "The Go Programming Language",
    "Authors": "Alan A. A. Donovan, Brian W. Kernighan",
    "Price": "$34.57"
  }
]

獲取:
go get github.com/goji/httpauth

package main

import (
    "net/http"

    "github.com/goji/httpauth"
    "github.com/zenazn/goji"
)

func main() {

    goji.Use(httpauth.SimpleBasicAuth("dave", "password"))

    // myHandler requires HTTP Basic Auth to access
    goji.Get("/thing", myHandler)

    goji.Serve()
}

Goji+Mongodb搭建微服務

golang中如何使用mongodb,我們之前多次介紹過了,可以翻看之前的部落格。
啟動mongodb server:

mongod.exe --dbpath d:\mongodb_data\db

直接上程式碼:

package main

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

    "goji.io"
    "goji.io/pat"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

func ErrorWithJSON(w http.ResponseWriter, message string, code int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(code)
    fmt.Fprintf(w, "{message: %q}", message)
}

func ResponseWithJSON(w http.ResponseWriter, json []byte, code int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(code)
    w.Write(json)
}

type Book struct {
    ISBN    string   `json:"isbn"`
    Title   string   `json:"title"`
    Authors []string `json:"authors"`
    Price   string   `json:"price"`
}

func main() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()

    session.SetMode(mgo.Monotonic, true)
    ensureIndex(session)

    mux := goji.NewMux()
    mux.HandleFunc(pat.Get("/books"), allBooks(session))
    mux.HandleFunc(pat.Post("/books"), addBook(session))
    mux.HandleFunc(pat.Get("/books/:isbn"), bookByISBN(session))
    mux.HandleFunc(pat.Put("/books/:isbn"), updateBook(session))
    mux.HandleFunc(pat.Delete("/books/:isbn"), deleteBook(session))
    http.ListenAndServe("localhost:8080", mux)
}

func ensureIndex(s *mgo.Session) {
    session := s.Copy()
    defer session.Close()

    c := session.DB("store").C("books")

    index := mgo.Index{
        Key:        []string{"isbn"},
        Unique:     true,
        DropDups:   true,
        Background: true,
        Sparse:     true,
    }
    err := c.EnsureIndex(index)
    if err != nil {
        panic(err)
    }
}

func allBooks(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        session := s.Copy()
        defer session.Close()

        c := session.DB("store").C("books")

        var books []Book
        err := c.Find(bson.M{}).All(&books)
        if err != nil {
            ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
            log.Println("Failed get all books: ", err)
            return
        }

        respBody, err := json.MarshalIndent(books, "", "  ")
        if err != nil {
            log.Fatal(err)
        }

        ResponseWithJSON(w, respBody, http.StatusOK)
    }
}

func addBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        session := s.Copy()
        defer session.Close()

        var book Book
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&book)
        if err != nil {
            ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest)
            return
        }

        c := session.DB("store").C("books")

        err = c.Insert(book)
        if err != nil {
            if mgo.IsDup(err) {
                ErrorWithJSON(w, "Book with this ISBN already exists", http.StatusBadRequest)
                return
            }

            ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
            log.Println("Failed insert book: ", err)
            return
        }

        w.Header().Set("Content-Type", "application/json")
        w.Header().Set("Location", r.URL.Path+"/"+book.ISBN)
        w.WriteHeader(http.StatusCreated)
    }
}

func bookByISBN(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        session := s.Copy()
        defer session.Close()

        isbn := pat.Param(r, "isbn")

        c := session.DB("store").C("books")

        var book Book
        err := c.Find(bson.M{"isbn": isbn}).One(&book)
        if err != nil {
            ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
            log.Println("Failed find book: ", err)
            return
        }

        if book.ISBN == "" {
            ErrorWithJSON(w, "Book not found", http.StatusNotFound)
            return
        }

        respBody, err := json.MarshalIndent(book, "", "  ")
        if err != nil {
            log.Fatal(err)
        }

        ResponseWithJSON(w, respBody, http.StatusOK)
    }
}

func updateBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        session := s.Copy()
        defer session.Close()

        isbn := pat.Param(r, "isbn")

        var book Book
        decoder := json.NewDecoder(r.Body)
        err := decoder.Decode(&book)
        if err != nil {
            ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest)
            return
        }

        c := session.DB("store").C("books")

        err = c.Update(bson.M{"isbn": isbn}, &book)
        if err != nil {
            switch err {
            default:
                ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
                log.Println("Failed update book: ", err)
                return
            case mgo.ErrNotFound:
                ErrorWithJSON(w, "Book not found", http.StatusNotFound)
                return
            }
        }

        w.WriteHeader(http.StatusNoContent)
    }
}

func deleteBook(s *mgo.Session) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        session := s.Copy()
        defer session.Close()

        isbn := pat.Param(r, "isbn")

        c := session.DB("store").C("books")

        err := c.Remove(bson.M{"isbn": isbn})
        if err != nil {
            switch err {
            default:
                ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
                log.Println("Failed delete book: ", err)
                return
            case mgo.ErrNotFound:
                ErrorWithJSON(w, "Book not found", http.StatusNotFound)
                return
            }
        }

        w.WriteHeader(http.StatusNoContent)
    }
}

執行,使用curl post資料:

curl -X POST -H "Content-Type: application/json" -d '{"isbn": "0134190440", "title": "The Go Programming Language", "authors": ["Alan A. A. Donovan", "Brian W. Kernighan"], "price":   "$34.57"}' http://localhost:8080/books

讀取:

curl -H "Content-Type: application/json" http://localhost:8080/books

返回:

[
  {
    "isbn": "0134190440",
    "title": "The Go Programming Language",
    "authors": [
      "Alan A. A. Donovan",
      "Brian W. Kernighan"
    ],
    "price": "$34.57"
  }
]

這裡寫圖片描述