標準庫 http 包的簡單實用

iamcyan發表於2020-12-27

通過幾個例子來學習http包的簡單實用

構建一個靜態的檔案伺服器

func FileServer(root FileSystem) Handler

如果需要使用作業系統的檔案系統,可以使用http.Dir

http.Handler(http.FileServer(http.Dir("/tmp")))

Example

package main

import (
    "log"
    "net/http"
)

func main() {
    log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("/Users/iamcyan/go_project/demo"))))
}

本地請求結果:

➜  demo pwd
/Users/iamcyan/go_project/demo
➜  demo tree
.
├── fmt.go
├── http.go
└── test
    └── a.txt

1 directory, 3 files
➜  demo curl http://localhost:8080/
<pre>
<a href=".idea/">.idea/</a>
<a href="fmt.go">fmt.go</a>
<a href="http.go">http.go</a>
<a href="test/">test/</a>
</pre>
➜  demo curl http://localhost:8080/test/
<pre>
<a href="a.txt">a.txt</a>
</pre>
➜  demo curl http://localhost:8080/test/a.txt
111

可以靜態檔案的訪問設定路由字首,比如我們給前面的路由加上字首、doc/,能夠返回同樣的結果,程式碼如下:

package main

import (
    "log"
    "net/http"
)

func main() {
    http.Handle("/doc/", http.StripPrefix("/doc/", http.FileServer(http.Dir("/Users/iamcyan/go_project/demo"))))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

返回示例:

➜  demo pwd
/Users/iamcyan/go_project/demo
➜  demo tree
.
├── fmt.go
├── http.go
└── test
    └── a.txt

1 directory, 3 files
➜  demo curl http://localhost:8080/doc/
<pre>
<a href=".idea/">.idea/</a>
<a href="fmt.go">fmt.go</a>
<a href="http.go">http.go</a>
<a href="test/">test/</a>
</pre>
➜  demo curl http://localhost:8080/doc/test/
<pre>
<a href="a.txt">a.txt</a>
</pre>
➜  demo

傳送GET請求

func Get(url string) (resp *Response, err error)
返回下列code時,進行重定向,最大的重定向次數為10次。

301 (Moved Permanently)
302 (Found)
303 (See Other)
307 (Temporary Redirect)
308 (Permanent Redirect)

Example

func main() {
   resp, err := http.Get("http://www.google.com/robots.txt")
   if err != nil {
      log.Fatal(err)
   }
   robots, err := ioutil.ReadAll(resp.Body)
   resp.Body.Close()
   if err != nil {
      log.Fatal(err)
   }
   fmt.Printf("%s", robots)
}

注意:body 如何讀取、body 讀取完成之後必須關閉

Handle

handle方法可以將路由處理handler註冊進DefaultServeMux

type countHandler struct {
    mu sync.Mutex
    c int
}

func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)  {
    h.mu.Lock()
    defer h.mu.Unlock()
    h.c++
    fmt.Fprintf(w, "count is %d\n", h.c)
}

func main() {
    http.Handle("/count", new(countHandler))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

HandleFunc

handle方法可以將路由處理handler function註冊進DefaultServeMux

package main

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

func main() {
    h1 := func(w http.ResponseWriter, _ *http.Request) {
        io.WriteString(w, "Hello from a HandleFunc #1!\n")
    }
    h2 := func(w http.ResponseWriter, _ *http.Request) {
        io.WriteString(w, "Hello from a HandleFunc #2!\n")
    }

    http.HandleFunc("/", h1)
    http.HandleFunc("/endpoint", h2)

    log.Fatal(http.ListenAndServe(":8080", nil))
}

ListenAndServe

func ListenAndServe(addr string, handler Handler) error
監聽網路埠,當連線到達時會呼叫handler處理程式。會開啟TCP keep-alive。
預設的handlernil,在這種情況下DefaultServeMux會被呼叫。

NotFoundHandler

func NotFoundHandler() Handler
Example

func main() {
    log.Fatal(http.ListenAndServe(":8080", http.NotFoundHandler()))
}

type ResponseWriter

ResponseWriter 介面用於 http 處理器生成HTTP 返回
Handler.ServerHTTP 返回後,ResponseWriter 不在被使用

實現改介面,需要實現3個方法

type ResponseWriter interfack {
    //設定http返回頭,如 Header().Set()
    Header() Header
    Write([]byte) (int, err)
    //傳送http狀態碼
    WriteHeader(statusCode int)
}

Example

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
        // Before any call to WriteHeader or Write, declare
        // the trailers you will set during the HTTP
        // response. These three headers are actually sent in
        // the trailer.
        w.Header().Set("Trailer", "AtEnd1, AtEnd2")
        w.Header().Add("Trailer", "AtEnd3")

        w.Header().Set("Content-Type", "text/plain; charset=utf-8") // normal header
        w.WriteHeader(http.StatusOK)

        w.Header().Set("AtEnd1", "value 1")
        io.WriteString(w, "This HTTP response has both headers before this text and trailers at the end.\n")
        w.Header().Set("AtEnd2", "value 2")
        w.Header().Set("AtEnd3", "value 3") // These will appear as trailers.
    })
}

請求返回:

➜  demo curl -v http://localhost:8080/test
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Trailer: AtEnd1, AtEnd2
< Trailer: AtEnd3
< Date: Sun, 27 Dec 2020 05:36:51 GMT
< Transfer-Encoding: chunked
<
This HTTP response has both headers before this text and trailers at the end.
* Connection #0 to host localhost left intact
* Closing connection 0

func(*ServeMux) Handle

func (mux *ServeMux) Handle(pattern string, handler Handler)
type apiHandler struct{}

func (apiHandler) ServeHTTP(http.ResponseWriter, *http.Request) {}

func main() {
    mux := http.NewServeMux()
    mux.Handle("/api/", apiHandler{})
    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        // The "/" pattern matches everything, so we need to check
        // that we're at the root here.
        if req.URL.Path != "/" {
            http.NotFound(w, req)
            return
        }
        fmt.Fprintf(w, "Welcome to the home page!")
    })
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章