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(¬e)
    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(¬eToUpd)
    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的开发.

你可能感兴趣的:(Go web server开发学习2)