Go web server開發學習2

  • DefaultServeMux

DefaultServeMux在http包使用的時候初始化

var DefaultServeMux = NewServeMux()

func NewServeMux() *ServeMux{return &ServeMux{m:make(map[string]muxEntry)}}

http包使用DefaultServeMux,實現了http.Handle和http.HandleFunc的簡寫方式.http.Handle方法在DefaultServeMux注冊了handler,而http.HandleFunc在DefautServeMux注冊了一個返回值是http.Handler的方法.所以這兩個方式都是在DefaultServeMux簡易的使用了ServeMux.Handle和ServeMux.HandleFunc;

ListenAndServe方法的第二個參數如果是nil,就會調用DefaultServeMux,提供一個http.Handler對象;

使用DefaultServeMux例子:

package main

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

func messageHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "歡迎使用Go!")
}

func main() {
    http.HandleFunc("/welcome", messageHandler)

    log.Println("Listening...")
    http.ListenAndServe(":9090", mux)
}

  • http.Serve 結構體

在前面的例子中,運行HTTP服務器就調用http.ListenAndServe;缺憾就是不能手動配置服務器的設置. http包提供了Serve結構體可以讓開發者自定義服務器的參數.

go源碼

type Server struct {
    Addr           string        // TCP address to listen on, ":http" if empty
    Handler        Handler       // handler to invoke, http.DefaultServeMux if nil
    ReadTimeout    time.Duration // maximum duration before timing out read of the request
    WriteTimeout   time.Duration // maximum duration before timing out write of the response
    MaxHeaderBytes int           // maximum size of request headers, DefaultMaxHeaderBytes if 0
    TLSConfig      *tls.Config   // optional TLS config, used by ListenAndServeTLS

    // TLSNextProto optionally specifies a function to take over
    // ownership of the provided TLS connection when an NPN
    // protocol upgrade has occurred.  The map key is the protocol
    // name negotiated. The Handler argument should be used to
    // handle HTTP requests and will initialize the Request's TLS
    // and RemoteAddr if not already set.  The connection is
    // automatically closed when the function returns.
    // If TLSNextProto is nil, HTTP/2 support is enabled automatically.
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)

    // ConnState specifies an optional callback function that is
    // called when a client connection changes state. See the
    // ConnState type and associated constants for details.
    ConnState func(net.Conn, ConnState)

    // ErrorLog specifies an optional logger for errors accepting
    // connections and unexpected behavior from handlers.
    // If nil, logging goes to os.Stderr via the log package's
    // standard logger.
    ErrorLog *log.Logger

    disableKeepAlives int32     // accessed atomically.
    nextProtoOnce     sync.Once // guards initialization of TLSNextProto in Serve
    nextProtoErr      error
}

允許設置error日志,最大最小超時時間,請求頭字節

使用http.Server的例子

package main

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

func messageHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "歡迎使用Go!")
}

func main() {
    http.HandleFunc("/welcome", messageHandler)

    server := &http.Server{
        Addr:           ":9090",
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    log.Println("Listening...")
    server.ListenAndServe()
}

自定義的server調用ListenAndServe()方法啟動服務器.

  • 第三方庫 Gorilla Mux

http.ServeMux 在很多情況下都能夠適應請求的多路由,在前面的多個例子中都用到,但當我們需要更加靈活的路由時,自帶的就可能不能滿足需求了,需要尋求第三庫.比如我們要RESTful API時.

Gorilla Mux允許自定義路由.當要建立RESTful服務時,和自帶的http.ServeMux對比就能感受到差別.

使用Gorilla Mux的大致模樣

func main() {
    r := mux.NewRouter().StrictSlash(false)
    r.HandleFunc("/api/notes", GetNoteHandler).Methods("GET")
    r.HandleFunc("/api/notes", PostNoteHandler).Methods("POST")

    server := &http.Server{
        Addr:    ":9090",
        Handler: r,
    }
    server.ListenAndServe()
}

一個mux.Router對象通過調用NewRouter方法創建,之后導向路由的資源.

當指定到一個URI參數時,可以和http請求匹配,這在建立RESTful應用的時候非常有用. 因為mux包實現了http.Handler 接口,可以容易的和http標準庫結合使用.可以非常容易的拓展開發出自己的包或者第三方庫.

和其它的web組合系統不同,Go的web開發合適的方式是:拓展基礎功能結合第三方庫;當選擇第三方庫,最好選擇和標準庫融合度較好的.Gorilla Mux就是一個很好的例子.

  • 使用RESTful API
// RESTful
package main

import (
    "encoding/json"
    "log"
    "net/http"
    "strconv"
    "time"

    "github.com/gorilla/mux"
)

type Note struct {
    Title       string    `json:"title"`
    Description string    `json:"description"`
    CreateOn    time.Time `json:"createon"`
}

//保存notes
var noteStore = make(map[string]Note)

//每個對象的id
var id int = 0

//HTTP GET - /api/notes
func GetNoteHandler(w http.ResponseWriter, r *http.Request) {
    var notes []Note
    for _, v := range noteStore {
        notes = append(notes, v)
    }

    w.Header().Set("Content-Type", "application/json")
    j, err := json.Marshal(notes)
    if err != nil {
        panic(err)
    }
    w.WriteHeader(http.StatusOK)
    w.Write(j)
}

//HTTP Post /api/notes

func PostNoteHandler(w http.ResponseWriter, r *http.Request) {
    var note Note
    err := json.NewDecoder(r.Body).Decode(&note)
    if err != nil {
        panic(err)
    }
    note.CreateOn = time.Now()
    id++
    k := strconv.Itoa(id)
    noteStore[k] = note

    j, err := json.Marshal(note)
    if err != nil {
        panic(err)
    }

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    w.Write(j)
}

//HTTP Put - /api/notes/{id}
func PutNoteHandler(w http.ResponseWriter, r *http.Request) {
    var err error
    vars := mux.Vars(r)
    k := vars["id"]

    var noteToUpd Note
    err = json.NewDecoder(r.Body).Decode(&noteToUpd)
    if err != nil {
        panic(err)
    }

    if note, ok := noteStore[k]; ok {
        noteToUpd.CreateOn = note.CreateOn
        delete(noteStore, k)
        noteStore[k] = noteToUpd
    } else {
        log.Printf("Could not find key of Note %s to delete", k)
    }
    w.WriteHeader(http.StatusNoContent)
}

//HTTP Delete - /api/notes/{id}
func DeleteNoteHandler(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    k := vars["id"]
    if _, ok := noteStore[k]; ok {
        delete(noteStore, k)
    } else {
        log.Printf("Could not find key of Note %s to delete", k)
    }
    w.WriteHeader(http.StatusNoContent)
}

func main() {
    r := mux.NewRouter().StrictSlash(false)
    r.HandleFunc("/api/notes", GetNoteHandler).Methods("GET")
    r.HandleFunc("/api/notes", PostNoteHandler).Methods("POST")
    r.HandleFunc("/api/notes/{id}", PutNoteHandler).Methods("PUT")
    r.HandleFunc("/api/notes/{id}", DeleteNoteHandler).Methods("DELETE")

    server := &http.Server{
        Addr:    ":9090",
        Handler: r,
    }
    log.Println("Listeing...")
    server.ListenAndServe()
}

  • 數據模型和存儲

上面的例子使用簡單的CRUD操作數據模型Note,建立簡單的REST API

type Note struct {
    Title       string    `json:"title"`
    Description string    `json:"description"`
    CreateOn    time.Time `json:"createon"`
}

對應JSON類型的數據API,結構體字段被編碼成json作為相應發送到客戶端.可以很輕松的將struct和json之間相互轉換,還可以自定義json的字段名.

在上面的例子,還沒有用到數據庫持久化數據,只是把數據保存到一個字典中;

  • 配置路由

使用mux包作為路由,配置handler.因為mux支持HTTP方法的映射,可以輕松的使用RESTful的方式展示數據源.

//程序的入口點
func main() {
    r := mux.NewRouter().StrictSlash(false)
    r.HandleFunc("/api/notes", GetNoteHandler).Methods("GET")
    r.HandleFunc("/api/notes", PostNoteHandler).Methods("POST")
    r.HandleFunc("/api/notes/{id}", PutNoteHandler).Methods("PUT")
    r.HandleFunc("/api/notes/{id}", DeleteNoteHandler).Methods("DELETE")

    server := &http.Server{
        Addr:    ":9090",
        Handler: r,
    }
    log.Println("Listeing...")
    server.ListenAndServe()
}

  • Handler函數來作CRUD操作
//HTTP GET - /api/notes
func GetNoteHandler(w http.ResponseWriter, r *http.Request) {
    var notes []Note
    for _, v := range noteStore {
        notes = append(notes, v)
    }

    w.Header().Set("Content-Type", "application/json")
    j, err := json.Marshal(notes)
    if err != nil {
        panic(err)
    }
    w.WriteHeader(http.StatusOK)
    w.Write(j)
}

在這里,先沒救了noteStore字典,把值添加到臨時的一個slice中(notes),通過調用json包下的Marshal方法,把notes切片轉換成json數據.

當我們訪問這個API時,不出問題就能得到類似的json結果:

[
    {
        "title": "hello",
        "description": "Hello,Go is awesome",
        "createon": "2016-07-27T14:07:15.314297691+08:00"
    }
]

小結:目前學習了基本的web開發和RESTful API的開發.
Go對應web,后端系統,特別是建立RESTful APIs的出色表現,是非常好的技術棧選擇.net/http包提供了構建web應用的基礎模塊,拓展基礎的功能,可以開發出第三方庫和自己的庫.

net/http包HTTP請求的主要的兩個模塊:http.ServeMux和http.Handler.對應了請求的路由和相應操作;

最后還進行了基于json數據的RESTful API的開發.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,287評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • github地址,歡迎大家提交更新。 express() express()用來創建一個Express的程序。ex...
    Programmer客棧閱讀 2,590評論 0 1
  • 是什么,是生命不能承受的,是“失去”嗎?那“失去”的具體含義又是什么呢?有人說是愛情,有人說是責任,有人說是對生活...
    安靜的等待中閱讀 345評論 0 0
  • 從不曾想,只是做了十二分鐘的無氧塑形運動,我的汗已經止不住了,我身體的局部是多么缺乏運動啊,只是針對性做了簡短運動...
    Sophia索菲閱讀 244評論 0 0