您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何理解Go語言中http和mysql,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
Go 原生支持http:
import "net/http"
Go 的http服務(wù)性能和nginx比較接近:
就是說用Go寫的Web程序上線,程序前面不需要再部署nginx的Web服務(wù)器,這里省掉的是Web服務(wù)器。如果服務(wù)器上部署了多個Web應(yīng)用,還是需要反向代理的,一般這也是nginx或apache。
幾行代碼就可以實現(xiàn)一個web服務(wù):
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 常見的請求方法:
Get請求
Post請求
Put請求
Delete請求
Head請求
Get 請求
使用Get請求網(wǎng)站的示例:
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請求只返回響應(yīng)頭。如果只想要獲取一些狀態(tài)信息的話,可以用Head請求。這樣避免返回響應(yīng)體,響應(yīng)體的數(shù)據(jù)是比較多的,適合做監(jiān)控。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.StatusContinue = 100
http.StatusOK = 200
http.StatusFound = 302 跳轉(zhuǎn)
http.StatusBadRequest = 400 非法請求
http.StatusUnanthorized = 401 沒有權(quán)限
http.StatusForbidden = 403 禁止訪問
http.Status.NotFound = 404 頁面不存在
http.StatusInternalServerError = 500 內(nèi)部錯誤
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]) // 所以這是一個數(shù)組 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("監(jiān)聽端口ERROR:", err) } }
如果處理函數(shù)里有panic,會導(dǎo)致整個程序崩潰,所以要 defer revoer() 來處理 panic。在處理函數(shù)開頭defer一個匿名函數(shù):
func FormServer(w http.ResponseWriter, request *http.Request) { // 增加一個defer來處理panic defer func() { if x := recover(); x != nil { log.Println(request.RemoteAddr, "捕獲到異常:", x) } }() // 原本的處理函數(shù)的內(nèi)容 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)) }
優(yōu)化統(tǒng)一處理
按照上面的做法,要在每個處理函數(shù)的開頭都加上panic的處理。由于每個處理函數(shù)的panic處理方法都一樣,所以可以寫一個自定義的處理函數(shù):
// 自定義的panic處理的函數(shù) 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,再接著下面調(diào)用業(yè)務(wù)邏輯 handle(writer, request) } } func main() { // http.HandleFunc("/form", FormServer) // 修改調(diào)用處理函數(shù)的方法 http.HandleFunc("/form", logPanics(FormServer)) // 把處理函數(shù)傳給自己寫的封裝了panic處理的函數(shù)里 if err := http.ListenAndServe(":8000", nil); err != nil { fmt.Println("監(jiān)聽端口ERROR:", err) } }
原本直接調(diào)用處理函數(shù)?,F(xiàn)在調(diào)用自定義的函數(shù),把處理函數(shù)傳進去。在自定義的函數(shù)里先加載defer,然后再調(diào)用執(zhí)行原本的處理函數(shù)。邏輯很簡單,就是把處理函數(shù)作為參數(shù)傳給自定義的函數(shù),在自定義的函數(shù)里再調(diào)用處理函數(shù)。在自定義的函數(shù)里寫上defer,這樣就相當(dāng)于所有的處理函數(shù)都有defer了。
使用模板需要用到 "text/template" 包。然后調(diào)用模板的t.Execute()方法輸出。
先準(zhǔn)備一個簡單的模板:
<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("模板加載數(shù)據(jù)異常:", err) } } /* 執(zhí)行結(jié)果 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> */
如果直接用 {{.}} 不加字段名的話,就是輸出結(jié)構(gòu)體打印的效果。
輸出到瀏覽器里
要輸出到瀏覽器里,只需要在 t.Execute(os.Stdout, p) 里,把原本輸出到終端換成輸出到處理函數(shù)的 w http.ResponseWriter 類型,就好了。
html模板的內(nèi)容不變,下面是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語句就是創(chuàng)建一個封閉的作用域,在其范圍內(nèi),{{.}}代表with的變量,而與外面的{{.}}無關(guān),只與with的參數(shù)有關(guān):
<body> {{with .Name}} <p>{{.}}</p> {{end}} </body>
上面這樣包在 {{with .Var}} 里,with 里的 {{.}} 代表的就是 Var 這個變量。
with 可以封裝常數(shù):
{{ with "world"}} Now the dot is set to {{ . }} {{ end }}
golang的template支持range循環(huán)來遍歷map、slice內(nèi)的內(nèi)容,在range循環(huán)內(nèi),還可以使用$設(shè)置循環(huán)變量,我們可以通過 $i $v 來訪問遍歷的值。語法為:
{{range $i, $v := .slice}} <li>key: {{ $key }}, value: {{ $value }}</li> {{end}}
這是另外一種遍歷方式,這種方式無法訪問到index或者key的值,需要通過點來訪問對應(yīng)的value:
{{range .slice}} {{.field}} {{end}}
在循環(huán)內(nèi),點是代表遍歷的值。原本使用點來訪問的變量,那么在循環(huán)內(nèi)部就要用 $. 來訪問。下面的例子表示循環(huán)內(nèi)和循環(huán)外 ArticleConten 這個變量訪問的方式:
{{.ArticleContent}} {{range .slice}} {{$.ArticleContent}} {{end}}
模板的參數(shù)可以是go中的基本數(shù)據(jù)類型,如字串,數(shù)字,布爾值,數(shù)組切片或者一個結(jié)構(gòu)體。在模板中設(shè)置變量可以使用 $variable := value。我們在range迭代的過程使用了設(shè)置變量的方式。
{{$article := "hello"}} {{$name := .Name}}
這里只簡單講了數(shù)據(jù)的增刪改查,所以測試代碼前,需要先把數(shù)據(jù)庫準(zhǔn)備好。
先創(chuàng)建一個數(shù)據(jù)庫,指定了編碼,這樣應(yīng)該可以支持中文:
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)數(shù)據(jù)庫接口。
sql 包必須與數(shù)據(jù)庫驅(qū)動結(jié)合使用。
驅(qū)動包需要安裝:
go get -u github.com/go-sql-driver/mysql
使用前,先要導(dǎo)入mysql的包:
import ( "database/sql" _ "github.com/go-sql-driver/mysql" )
上面導(dǎo)入了2個包。第一個是sql包,就是我們調(diào)用操作數(shù)據(jù)庫用的。
第二個是驅(qū)動包,這里前面加了占位符,所以這個包只是引入,但是不使用它。并且如果要操作別的數(shù)據(jù)庫的話,只需要修改驅(qū)動包就行了。其實就是只執(zhí)行這個包里的初始化init函數(shù)
構(gòu)建連接, 格式是:”用戶名:密碼@tcp(IP:端口)/數(shù)據(jù)庫?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("連接數(shù)據(jù)庫失敗:", err) return } DB = database } func main() { fmt.Println(DB) DB.SetMaxIdleConns(16) //設(shè)置閑置連接數(shù) DB.SetMaxOpenConns(100) //設(shè)置最大連接數(shù) DB.SetConnMaxLifetime(100*time.Second) //最大連接周期,超過時間的連接就close fmt.Println(DB) }
下面是插入數(shù)據(jù),并且再獲取id的示例:
// 數(shù)據(jù)庫連接的init函數(shù)就省略了 func insert() { r, err := DB.Exec("insert into person(username,gender,email) values(?,?,?)", "Barry", "Male", "Barry@go.net") if err != nil { fmt.Println("插入數(shù)據(jù)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(?,?,?) 里的問號,是占位符,具體的值可以寫在后面的參數(shù)里。當(dāng)然如果不用占位符,直接就傳1個字符串作為參數(shù)也是可以的。
查詢單個字段:
func query() { row := DB.QueryRow("select username from person where user_id=?", 1) var name string // 創(chuàng)建變量用于存放查詢到的數(shù)據(jù) if err := row.Scan(&name); err != nil { fmt.Println("Scan Failed:", err) return } fmt.Println(name) } func main() { query() }
也可以一次查詢多個字段或所有字段,查詢之前按照表的類型創(chuàng)建結(jié)構(gòu)體,用查詢到的數(shù)據(jù)為結(jié)構(gòu)體賦值:
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中的字段必須是按照數(shù)據(jù)庫存入字段的順序,否則報錯 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() }
數(shù)據(jù)模型,就是上面定義的結(jié)構(gòu)體。這里的類型可以是Go的標(biāo)準(zhǔn)數(shù)據(jù)類型。但是如果數(shù)據(jù)庫的字段允許為空,并且該字段的值也為空,那么查詢后該字段會返回nil。如果是string類型,則無法接收nil,但sql.NullString則可以接收nil值。
另外,結(jié)構(gòu)體里的tag標(biāo)簽在這里沒有意義。不過上面的tag標(biāo)注了該字段在數(shù)據(jù)庫里對應(yīng)的字段名,可能在別處會有用。
查詢多行
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 更新數(shù)據(jù)
result, err := DB.Exec("UPDATE person set email=? where username=?", "Cara", "Cara@catco.org")
delete 刪除數(shù)據(jù)
result,err := DB.Exec("DELETE FROM person where id=?",1)
注意:更新數(shù)據(jù)不返回LastInsertID,所以result.LastInsertID一直為0。刪除數(shù)據(jù)可以拿到LastInsertID,用法和插入數(shù)據(jù)里一樣。
sqlx是一個go語言包,在內(nèi)置database/sql包之上增加了很多擴展,簡化數(shù)據(jù)庫操作代碼的書寫。
由于database/sql接口是sqlx的子集,所有database/sql的用法,在sqlx中一樣可以用。不過sqlx還有更多擴展,用起來更方便。
安裝:
go get github.com/jmoiron/sqlx
查詢 Select() 方法
Select是一個非常省時的擴展。它們把query和非常靈活的scan語法結(jié)合起來。Select用來獲取結(jié)果切片:
// 這里的tag標(biāo)簽就有意義了,下面的Select()方法應(yīng)該就是根據(jù)tag標(biāo)簽對號入座的 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 // 這里創(chuàng)建的是存放結(jié)構(gòu)體的切片 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 數(shù)據(jù)庫開發(fā)神器,這里就提一下,等到真正用的時候再去深入學(xué)習(xí)了。
事務(wù)的特性:
原子性
一致性
隔離性
持久性
實現(xiàn)事務(wù)的步驟
import “github.com/jmoiron/sqlx"
:導(dǎo)入第三方庫 sqlx
Db.Begin()
:聲明事務(wù)。寫在操作的開頭
Db.Submit()
:所有事務(wù)完成后,提交事務(wù)。寫在操作的結(jié)尾
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() }
關(guān)于如何理解Go語言中http和mysql就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。