亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何理解Go語言中http和mysql

發布時間:2021-10-25 16:50:40 來源:億速云 閱讀:134 作者:柒染 欄目:編程語言

這篇文章給大家介紹如何理解Go語言中http和mysql,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

http 編程

Go 原生支持http:

import "net/http"

Go 的http服務性能和nginx比較接近:
就是說用Go寫的Web程序上線,程序前面不需要再部署nginx的Web服務器,這里省掉的是Web服務器。如果服務器上部署了多個Web應用,還是需要反向代理的,一般這也是nginx或apache。

幾行代碼就可以實現一個web服務:

package main

import (
    "fmt"
    "net/http"
)

func Hello(w http.ResponseWriter, r *http.Request) {
    fmt.Println(*r)
    fmt.Fprintf(w, "Hello World")
}

func main() {
    http.HandleFunc("/", Hello)
    err := http.ListenAndServe("0.0.0.0:8000", nil)
    if err != nil {
        fmt.Println("http Listen failed")
    }
}

http client

http 常見的請求方法:

  • Get請求

  • Post請求

  • Put請求

  • Delete請求

  • Head請求

Get 請求
使用Get請求網站的示例:

package main

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

func main() {
    res, err := http.Get("http://edu.51cto.com")
    if err != nil {
        fmt.Println("http get ERRPR:", err)
        return
    }
    data, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println("get data ERROR:", err)
        return
    }
    fmt.Println(string(data))
}

Head請求
Head請求只返回響應頭。如果只想要獲取一些狀態信息的話,可以用Head請求。這樣避免返回響應體,響應體的數據是比較多的,適合做監控。Head請求的示例:

package main

import (
    "fmt"
    "net/http"
)

var urls = []string{
    "http://×××w.baidu.com",
    "http://×××w.google.com",
    "http://×××w.sina.com.cn",
    "http://×××w.163.com",
}

func main() {
    for _, v := range urls {
        resp, err := http.Head(v)
        if err != nil {
            fmt.Println("Head request ERROR:", err)
            continue
        }
        fmt.Println(resp.Status)
    }
}

http 常見狀態碼

http.StatusContinue = 100
http.StatusOK = 200
http.StatusFound = 302  跳轉
http.StatusBadRequest = 400  非法請求
http.StatusUnanthorized = 401  沒有權限
http.StatusForbidden = 403  禁止訪問
http.Status.NotFound = 404  頁面不存在
http.StatusInternalServerError = 500  內部錯誤

處理form表單

package main

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

const form = `
<html>
<body>
<form action="#" method="post" name="bar">
    <input type="text" name="in" />
    <input type="text" name="in" />
    <input type="submit" value="Submit" />
</form>
</body>
</html>`

func FormServer(w http.ResponseWriter, request *http.Request) {
    w.Header().Set("content-Type", "text/html")
    switch request.Method {
    case "GET":
        io.WriteString(w, form)
    case "POST":
        request.ParseForm()
        io.WriteString(w, request.Form["in"][0])  // 注意上面的2個input的name是一樣的
        io.WriteString(w, request.Form["in"][1])  // 所以這是一個數組
        io.WriteString(w, "</br>")
        io.WriteString(w, request.FormValue("in"))  // 一般去一個值,就用這個方法
    }
}

func main() {
    http.HandleFunc("/form", FormServer)
    if err := http.ListenAndServe(":8000", nil); err != nil {
        fmt.Println("監聽端口ERROR:", err)
    }
}

panic 處理

如果處理函數里有panic,會導致整個程序崩潰,所以要 defer revoer() 來處理 panic。在處理函數開頭defer一個匿名函數:

func FormServer(w http.ResponseWriter, request *http.Request) {
    // 增加一個defer來處理panic
    defer func() {
        if x := recover(); x != nil {
            log.Println(request.RemoteAddr, "捕獲到異常:", x)
        }
    }()
    // 原本的處理函數的內容
    w.Header().Set("content-Type", "text/html")
    switch request.Method {
    case "GET":
        io.WriteString(w, form)
    case "POST":
        request.ParseForm()
        io.WriteString(w, request.FormValue("in"))  // 一般去一個值,就用這個方法
    }
    // 搞個panic出來
    zero := 0
    tmp := 1 / zero
    io.WriteString(w, string(tmp))
}

優化統一處理
按照上面的做法,要在每個處理函數的開頭都加上panic的處理。由于每個處理函數的panic處理方法都一樣,所以可以寫一個自定義的處理函數:

// 自定義的panic處理的函數
func logPanics(handle http.HandlerFunc) http.HandlerFunc {
    return func(writer http.ResponseWriter, request *http.Request) {
        defer func() {
            if x := recover(); x != nil {
                log.Println(request.RemoteAddr, "捕獲到異常:", x)
            }
        }()
        // 上面先處理panic,再接著下面調用業務邏輯
        handle(writer, request)
    }
}

func main() {
    // http.HandleFunc("/form", FormServer)  // 修改調用處理函數的方法
    http.HandleFunc("/form", logPanics(FormServer))  // 把處理函數傳給自己寫的封裝了panic處理的函數里
    if err := http.ListenAndServe(":8000", nil); err != nil {
        fmt.Println("監聽端口ERROR:", err)
    }
}

原本直接調用處理函數。現在調用自定義的函數,把處理函數傳進去。在自定義的函數里先加載defer,然后再調用執行原本的處理函數。邏輯很簡單,就是把處理函數作為參數傳給自定義的函數,在自定義的函數里再調用處理函數。在自定義的函數里寫上defer,這樣就相當于所有的處理函數都有defer了。

模板

使用模板需要用到 "text/template" 包。然后調用模板的t.Execute()方法輸出。

替換

先準備一個簡單的模板:

<p>Hello {{.Name}}</p>
<p>Age: {{.Age}}</p>

然后在Go里使用模板:

package main

import (
    "fmt"
    "os"
    "text/template"
)

type Person struct {
    Name string
    Age int
}

func main() {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        fmt.Println("模板解析異常:", err)
        return
    }
    p := Person{"Bob", 32}
    if err := t.Execute(os.Stdout, p); err != nil {
        fmt.Println("模板加載數據異常:", err)
    }
}

/* 執行結果
PS H:\Go\src\go_dev\day10\http\use_template> go run main.go
<p>Hello Bob</p>
<p>Age: 32</p>
PS H:\Go\src\go_dev\day10\http\use_template>
*/

如果直接用 {{.}} 不加字段名的話,就是輸出結構體打印的效果。
輸出到瀏覽器里
要輸出到瀏覽器里,只需要在 t.Execute(os.Stdout, p) 里,把原本輸出到終端換成輸出到處理函數的 w http.ResponseWriter 類型,就好了。
html模板的內容不變,下面是go的代碼:

package main

import (
    "fmt"
    "net/http"
    "text/template"
)

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

type Person struct {
    Name string
    Age int
}

func Index(w http.ResponseWriter, r *http.Request) {
    p := Person{"Cara", 18}
    t, err := template.ParseFiles("index.html")
    if err != nil {
        fmt.Println("加載模板ERROR:", err)
        return
    }
    t.Execute(w, p)
}

func main() {
    http.HandleFunc("/", Hello)
    http.HandleFunc("/index", Index)
    err := http.ListenAndServe("0.0.0.0:8000", nil)
    if err != nil {
        fmt.Println("http Listen failed")
    }
}

判斷

用法示例:

<body>
{{if gt .Age 18}}
<p>已成年</p>
{{else}}
<p>未成年</p>
{{end}}
</body>

更多判斷邏輯:
not 非
{{if not .condition}}
{{end}}
and 與
{{if and .condition1 .condition2}}
{{end}}
or 或
{{if or .condition1 .condition2}}
{{end}}
eq 等于
{{if eq .var1 .var2}}
{{end}}
ne 不等于
{{if ne .var1 .var2}}
{{end}}
lt 小于
{{if lt .var1 .var2}}
{{end}}
le 小于等于
{{if le .var1 .var2}}
{{end}}
gt 大于
{{if gt .var1 .var2}}
{{end}}
ge 大于等于
{{if ge .var1 .var2}}
{{end}}

with 封裝

with語句就是創建一個封閉的作用域,在其范圍內,{{.}}代表with的變量,而與外面的{{.}}無關,只與with的參數有關:

<body>
{{with .Name}}
<p>{{.}}</p>
{{end}}
</body>

上面這樣包在 {{with .Var}} 里,with 里的 {{.}} 代表的就是 Var 這個變量。
with 可以封裝常數:

{{ with "world"}}
    Now the dot is set to {{ . }}
{{ end }}

循環(遍歷)

golang的template支持range循環來遍歷map、slice內的內容,在range循環內,還可以使用$設置循環變量,我們可以通過 $i $v 來訪問遍歷的值。語法為:

{{range $i, $v := .slice}}
    <li>key: {{ $key }}, value: {{ $value }}</li>
{{end}}

這是另外一種遍歷方式,這種方式無法訪問到index或者key的值,需要通過點來訪問對應的value:

{{range .slice}}
{{.field}}
{{end}}

在循環內,點是代表遍歷的值。原本使用點來訪問的變量,那么在循環內部就要用 $. 來訪問。下面的例子表示循環內和循環外 ArticleConten 這個變量訪問的方式:

{{.ArticleContent}}
{{range .slice}}
{{$.ArticleContent}}
{{end}}

定義變量

模板的參數可以是go中的基本數據類型,如字串,數字,布爾值,數組切片或者一個結構體。在模板中設置變量可以使用 $variable := value。我們在range迭代的過程使用了設置變量的方式。

{{$article := "hello"}}
{{$name := .Name}}

mysql 使用

這里只簡單講了數據的增刪改查,所以測試代碼前,需要先把數據庫準備好。
先創建一個數據庫,指定了編碼,這樣應該可以支持中文:

CREATE DATABASE 庫名 CHARSET "utf8";

然后建2張表:

CREATE TABLE person (
    user_id int primary key auto_increment,
    username varchar(260),
    gender varchar(260),
    email varchar(260)
);

CREATE TABLE place (
    country varchar(200),
    city varchar(200),
    telcode int
);

導入數據庫驅動

sql 包提供了通用的SQL(或類SQL)數據庫接口。
sql 包必須與數據庫驅動結合使用。

驅動包需要安裝:

go get -u github.com/go-sql-driver/mysql

使用前,先要導入mysql的包:

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

上面導入了2個包。第一個是sql包,就是我們調用操作數據庫用的。
第二個是驅動包,這里前面加了占位符,所以這個包只是引入,但是不使用它。并且如果要操作別的數據庫的話,只需要修改驅動包就行了。其實就是只執行這個包里的初始化init函數

連接數據庫

構建連接, 格式是:”用戶名:密碼@tcp(IP:端口)/數據庫?charset=utf8” :

package main

import (
    "fmt"
    "time"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

var DB *sql.DB

func init() {
    database, err := sql.Open("mysql", "admin:admin123@tcp(192.168.3.103:3306)/Golang_week10")
    if err != nil {
        fmt.Println("連接數據庫失敗:", err)
        return
    }
    DB = database
}

func main() {
    fmt.Println(DB)
    DB.SetMaxIdleConns(16)  //設置閑置連接數
    DB.SetMaxOpenConns(100)  //設置最大連接數
    DB.SetConnMaxLifetime(100*time.Second)  //最大連接周期,超過時間的連接就close
    fmt.Println(DB)
}

插入數據

下面是插入數據,并且再獲取id的示例:

// 數據庫連接的init函數就省略了
func insert() {
    r, err := DB.Exec("insert into person(username,gender,email) values(?,?,?)", "Barry", "Male", "Barry@go.net")
    if err != nil {
        fmt.Println("插入數據ERROR:", err)
        return
    }
    fmt.Println(r)
    id, err := r.LastInsertId()
    if err != nil {
        fmt.Println("獲取id ERROR:", err)
        return
    }
    fmt.Println(id)
}

func main() {
    insert()
}

上面的  values(?,?,?) 里的問號,是占位符,具體的值可以寫在后面的參數里。當然如果不用占位符,直接就傳1個字符串作為參數也是可以的。

查詢

查詢單個字段:

func query() {
    row := DB.QueryRow("select username from person where user_id=?", 1)
    var name string  // 創建變量用于存放查詢到的數據
    if err := row.Scan(&name); err != nil {
        fmt.Println("Scan Failed:", err)
        return
    }
    fmt.Println(name)
}

func main() {
    query()
}

也可以一次查詢多個字段或所有字段,查詢之前按照表的類型創建結構體,用查詢到的數據為結構體賦值:

type Person struct {
    ID int `db:"user_id"`
    Username sql.NullString `db:"username"`
    Gender sql.NullString `db:"gender"`
    Email sql.NullString `db:"email"`
}

func query() {
    row := DB.QueryRow("select * from person where user_id=?", 6)
    var person = new(Person)
    // row.scan中的字段必須是按照數據庫存入字段的順序,否則報錯
    if err := row.Scan(&person.ID, &person.Username, &person.Gender, &person.Email); err != nil {
        fmt.Println("Scan Failed:", err)
        return
    }
    fmt.Println(person)
}

func main() {
    query()
}

數據模型,就是上面定義的結構體。這里的類型可以是Go的標準數據類型。但是如果數據庫的字段允許為空,并且該字段的值也為空,那么查詢后該字段會返回nil。如果是string類型,則無法接收nil,但sql.NullString則可以接收nil值。
另外,結構體里的tag標簽在這里沒有意義。不過上面的tag標注了該字段在數據庫里對應的字段名,可能在別處會有用。

查詢多行

func query() {
    rows, err := DB.Query("select * from person where user_id > ?", 1)
    defer func() {
        if rows != nil {
            rows.Close()
        }
    }()
    if err != nil {
        fmt.Println("Query 查詢 ERROR:", err)
        return
    }
    var person = new(Person)
    for rows.Next() {
        if err = rows.Scan(&person.ID, &person.Username, &person.Gender, &person.Email); err != nil {
            fmt.Println("Scan Failed:", err)
            return
        }
        fmt.Println(person)
    }
}

func main() {
    query()
}

查詢用起來還是不太方法,不過還可以選擇其他第三方庫,那里會有一些很好的擴展。后面會舉例。

其他操作

由于基本都是用SQL的命令進行操作,所以其他操作就不一個一個舉例了
update 更新數據

result, err := DB.Exec("UPDATE person set email=? where username=?", "Cara", "Cara@catco.org")

delete 刪除數據

result,err := DB.Exec("DELETE FROM person where id=?",1)

注意:更新數據不返回LastInsertID,所以result.LastInsertID一直為0。刪除數據可以拿到LastInsertID,用法和插入數據里一樣。

第三方庫 sqlx

sqlx是一個go語言包,在內置database/sql包之上增加了很多擴展,簡化數據庫操作代碼的書寫。
由于database/sql接口是sqlx的子集,所有database/sql的用法,在sqlx中一樣可以用。不過sqlx還有更多擴展,用起來更方便。
安裝:

go get github.com/jmoiron/sqlx

查詢 Select() 方法
Select是一個非常省時的擴展。它們把query和非常靈活的scan語法結合起來。Select用來獲取結果切片:

// 這里的tag標簽就有意義了,下面的Select()方法應該就是根據tag標簽對號入座的
type Person struct {
    ID int `db:"user_id"`
    Username sql.NullString `db:"username"`
    Gender sql.NullString `db:"gender"`
    Email sql.NullString `db:"email"`
}

func select() {
    var persons []Person  // 這里創建的是存放結構體的切片
    if err := DB.Select(&person, "select * from person where userid > ?", 1); err != nil {
        fmt.Println("Select ERROR:", err)
        return
    }
    fmt.Println(person)
}

Select可以提高編碼效率,還有更多擴展。sqlx 號稱 golang 數據庫開發神器,這里就提一下,等到真正用的時候再去深入學習了。

mysql 事務

事務的特性:

  • 原子性

  • 一致性

  • 隔離性

  • 持久性

實現事務的步驟

  • import “github.com/jmoiron/sqlx" :導入第三方庫 sqlx

  • Db.Begin() :聲明事務。寫在操作的開頭

  • Db.Submit() :所有事務完成后,提交事務。寫在操作的結尾

  • Db.Rollback() :如果有失敗,回滾。寫在返回err的判斷語句里

Db.Begin() 方法返回了 conn 后,之后的操作都是對 conn 對象進行操作:

func main() {
    conn, err := Db.Begin()
    if err != nil {
        return
    }

    r, err := conn.Exec("insert into person(username, sex, email)values(?, ?, ?)", "stu001", "man", "stu01@qq.com")
    if err != nil {
        fmt.Println("exec failed, ", err)
        conn.Rollback()
        return
    }
    id, err := r.LastInsertId()
    if err != nil {
        fmt.Println("exec failed, ", err)
        conn.Rollback()
        return
    }
    r, err := conn.Exec("insert into person(username, sex, email)values(?, ?, ?)", "stu001", "man", "stu01@qq.com")
    if err != nil {
        fmt.Println("exec failed, ", err)
        conn.Rollback()
        return
    }

    fmt.Println("insert succ:", id)
    conn.Commit()
}

關于如何理解Go語言中http和mysql就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

调兵山市| 锦州市| 民丰县| 巴林左旗| 黔南| 康马县| 桂东县| 托里县| 井研县| 新巴尔虎左旗| 洪洞县| 元氏县| 玉门市| 璧山县| 鄂托克旗| 合川市| 麻江县| 政和县| 全南县| 会宁县| 华蓥市| 江安县| 洛浦县| 高尔夫| 额尔古纳市| 洛宁县| 乐清市| 蒙阴县| 郎溪县| 东乡县| 阿克| 湄潭县| 万盛区| 隆子县| 马山县| 驻马店市| 莆田市| 垦利县| 黔西| 潮州市| 紫阳县|