1. 程式人生 > >golang http 程式設計-1(伺服器程式設計)

golang http 程式設計-1(伺服器程式設計)

http 程式設計

http常見請求方法

1. Get 請求,請求一個頁面或者資訊

2. Post 請求, 提交一個表單給伺服器使用

3. Put 請求,建立一個資源,一般上傳檔案使用該方法

4. Delete請求,刪除一個資訊使用該方法

5. Head 請求,只請求http header,一般監測網站狀態使用該方法

6. OPTIONS 請求,一般監測網站支援的http方法列表

7. TRACE 請求(不常用),一般使用監測使用

8. LOCK 請求(不常用),允許使用者鎖定資源,比如可以再編輯某個資源時將其鎖定,以防別人同時對其進行編輯。

9. MKCOL 請求(不常用), 允許使用者建立資源

10. COPY 請求(不常用), 便於使用者在伺服器上覆制資源

11. MOVE 請求(不常用),在伺服器上移動資源

http常見狀態碼

狀態碼 http方法 描述
100 http.StatusContinue = 100 一般檔案上傳的時候使用
200 http.StatusOK = 200 伺服器狀態正常
302 http.StatusFound = 302 跳轉
400 http.StatusBadRequest = 400 非法請求構造協議包無法解析,伺服器無法解析處理
401 http.StatusUnauthorized = 401 許可權未通過
403 http.StatusForbidden = 403 不讓訪問該資源
404 http.StatusNotFound = 404 頁面不存在,資源未找到
500 http.StatusInternalServerError = 500 伺服器內部錯誤,無法處理請求,
502 php nginx請求php, php無響應nginx 返回502

簡單的http server

  • http.HandleFunc 定義url路由
  • http.stenAndServe 啟動server服務
package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Println("index hello")
    fmt.Fprintf(w, "<h1>index hello</h1>")

}

func login(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "user login")
}

func main() {

    http.HandleFunc("/", hello)
    http.HandleFunc("/user/login", login)
    err := http.ListenAndServe("0.0.0.0:8000", nil)
    if err != nil {
        fmt.Println("server failed, err:", err)
    }
}

編譯並執行

$ go build go_dev/day10/example/http_server
$ sudo ./http_server

開啟瀏覽器驗證路徑/, /user/login

http client (Get方法)

http.Get 來獲取一個url的request的物件,request.BoBy 是網頁資訊(html)


package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    res, err := http.Get("http://localhost:8000")
    if err != nil {
        fmt.Println("get err:", err)
        return
    }
    data, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println("read data err:", err)
        return
    }
    fmt.Printf("data: %s", string(data))
    fmt.Printf("data: %v\n", data)
}

執行:

%v是列印原始資訊,很顯然獲取過來的資訊是一個ASCII碼錶的數字對應的資訊

例如index ASCII表:[105 110 100 101 120 10], 10是換行符\n

$ .\2-http-client.exe
data: <h1>index</h1>
data: [60 104 49 62 105 110 100 101 120 60 47 104 49 62 10]

檢測http server 狀態(Head 方法)

檢測http伺服器狀態使用http方法 Head, 只獲取頭部資訊

package main

import (
    "fmt"
    "net/http"
)

var url = []string{
    "http://www.baidu.com",
    "http://taobao.com",
    "http://google.com",
}

func main() {
    for _, v := range url {

        c := http.Client{}

        res, err := http.Head(v)
        if err != nil {
            fmt.Println("get head failed, err:", err)
            continue
        }
        fmt.Printf("head success , status:%v\n", res.Status)
    }

}

編譯並執行

$ go build go_dev/day10/example/http_head
$ ./http_head 
head success , status:200 OK
head success , status:200 OK
get head failed, err: Head http://google.com: dial tcp 216.58.200.46:80: i/o timeout

發現超時時間過長,調整超時時間方法

一般檢測一個網站,一秒鐘沒有返回就認為改網站故障

調整程式碼(帶超時時間的http請求)

調整在net底層設定超時時間,這個設定比較底層


package main

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

var url = []string{
    "http://www.baidu.com",
    "http://google.com",
    "http://taobao.com",
}

func timeout(network, addr string) (net.Conn, error) {
    timeout := time.Second
    return net.DialTimeout(network, addr, timeout)

}

func main() {
    for _, v := range url {
        c := http.Client{
            Transport: &http.Transport{
                Dial: timeout,
            },
        }

        fmt.Println()
        fmt.Println(time.Now())
        res, err := c.Head(v)
        if err != nil {
            fmt.Println("get head failed, err:", err)
            continue
        }
        fmt.Printf("head success , status:%v\n", res.Status)
    }

}

更簡單方法調整超時時間

http.Client 結構體設定超時時間,這個更簡單

package main

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

var url = []string{
    "http://www.baidu.com",
    "http://www.google.com.hk",
    "http://www.jd.com",
}

func main() {

    client := http.Client{
        Timeout: time.Duration(time.Second),
    }
    for _, v := range url {
        fmt.Println()
        fmt.Println(time.Now())
        resp, err := client.Head(v)
        if err != nil {
            fmt.Printf("head %s failed, err:%v\n", v, err)
            continue
        }
        fmt.Printf("%s head success, status:%v\n", v, resp.Status)
    }
}

編譯並執行

$ go build go_dev/day10/example/http_head
$ ./http_head 
2017-08-08 23:18:35.329123548 +0800 CST
head success , status:200 OK

2017-08-08 23:18:35.339484242 +0800 CST
get head failed, err: Head http://google.com: dial tcp [2404:6800:4008:802::200e]:80: i/o timeout

2017-08-08 23:18:36.343658858 +0800 CST
head success , status:200 OK

網站檢測超時時間1秒

http 表單處理

  • http.Request.ParseFrom(), 解析from表單
  • http.Request.From , 返回一個數組,獲取裡面的值需要通過下標來獲取:request.Form["username"][0]
  • http.Request.FromValue(name), 獲取一個標籤為name的值
package main

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

const from = `
<html>
    <body>
        <form action="#" method="POST" name="bar">
            <input type="text" name="username" />
            <input type="text" name="password" />
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>
`

func simpleServer(w http.ResponseWriter, p *http.Request) {
    io.WriteString(w, "<h1>hello world</h1>")
}

func fromServer(w http.ResponseWriter, request *http.Request) {
    w.Header().Set("Content-Type", "text/html")

    switch request.Method {
    case "GET":
        io.WriteString(w, from)
    case "POST":
        // 解析form
        request.ParseForm()

        // request.From[username] 返回一個數組,必須要寫具體的那個下標的元素
        io.WriteString(w, request.Form["username"][0])
        io.WriteString(w, "\n")

        // request.FromValue 獲取一個指定的 name="password"的標籤的值
        io.WriteString(w, request.FormValue("password"))
    }
}

func main() {
    http.HandleFunc("/test", simpleServer)
    http.HandleFunc("/from", fromServer)

    err := http.ListenAndServe("0.0.0.0:8000", nil)
    if err != nil {
        fmt.Println("http server failed...")
        return
    }
}

編譯並執行

$ go build go_dev/day10/exercises/3-http-from
$ ./3-http-from

開啟瀏覽器測試是否提交的表單又重新寫到了返回的頁面了

http panic 處理

適合場景
- 其中一個頁面出現異常,不會引起整個web伺服器的異常退出
- 類似python的裝飾器等功能都可以使用這個

主要實現是在執行url函式前添加了一個異常捕捉的函式

package main

import (
    "fmt"
    "net/http"
)

func index(writer http.ResponseWriter, request *http.Request) {
    fmt.Println("page --> index")
    fmt.Fprintln(writer, "<h1>welcome index</h1>")
}

func login(writer http.ResponseWriter, request *http.Request) {
    panic("user logiun page panic...")

}
func logPanic(handle http.HandlerFunc) http.HandlerFunc {
    return func(writer http.ResponseWriter, request *http.Request) {
        defer func() {
            if x := recover(); x != nil {
                fmt.Printf("[%v] caught panic: %v\n", request.RemoteAddr, x)
            }

        }()
        handle(writer, request)

    }
}

func main() {
    http.HandleFunc("/", logPanic(index))
    http.HandleFunc("/login", logPanic(login))
    err := http.ListenAndServe("0.0.0.0:8000", nil)
    if err != nil {
        fmt.Println("server failed ...")
        return
    }
}

編譯並測試(訪問/login異常,但訪問/正常返回)

$ go build go_dev/day10/exercises/4-http-panic
$  .\4-http-panic.exe