Gin實戰:Gin+Mysql簡單的Restful風格的API


我們已經了解了Golang的Gin框架。對於Webservice服務,restful風格幾乎一統天下。Gin也天然的支持restful。下面就使用gin寫一個簡單的服務,麻雀雖小,五臟俱全。我們先以一個單文件開始,然后再逐步分解模塊成包,組織代碼。

Hello World

使用Gin的前提是安裝,我們需要安裝gin和mysql的驅動,具體的安裝方式就不在贅述。

創建一個文件夾用來為項目,新建一個文件main.go:

☁  newland  tree
.
└── main.go

main.go

package main

import (
 "gopkg.in/gin-gonic/gin.v1"
 "net/http"
)

func main() {
 router := gin.Default()

 router.GET("/", func(c *gin.Context) {
  c.String(http.StatusOK, "Hello world")
 })

 router.Run(":8801")
}

編譯運行

數據庫

安裝完畢框架,完成一次請求響應之后。接下來就是安裝數據庫驅動和初始化數據相關的操作了。首先,我們需要新建數據表。一個及其簡單的數據表:

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) DEFAULT NULL,
  `telephone` varchar(12) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4;

創建數據表之后,初始化數據庫連接池:

db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
 if err != nil{
  log.Fatalln(err)
 }
defer db.Close()
 
 
 db.SetMaxIdleConns(20)
 db.SetMaxOpenConns(20)
 
 if err := db.Ping(); err != nil{
  log.Fatalln(err)
 }
使用sql.Open方法會創建一個數據庫連接池db。這個db不是數據庫連接,它是一個連接池,只有當真正數據庫通信的時候才創建連接。例如這里的 db.Ping的操作。 db.SetMaxIdleConns(20)db.SetMaxOpenConns(20)分別設置數據庫的空閑連接和最大打開連接,即向Mysql服務端發出的所有連接的最大數目。
如果不設置,默認都是0,表示打開的連接沒有限制。我在壓測的時候,發現會存在大量的TIME_WAIT狀態的連接,雖然mysql的連接數沒有上升。設置了這兩個參數之后,不在存在大量TIME_WAIT狀態的連接了。而且qps也沒有明顯的變化,出於對數據庫的保護,最好設置這連個參數。

CURD 增刪改查

Restful的基本就是對資源的curd操作。下面開啟我們的第一個api接口,增加一個資源。

 
          
var db *sql.DB

type Person struct {
Id int `json:"id" form:"id"`
Name string `json:"name" form:"name"`
Telephone string `json:"telephone" form:"telephone"`
}
func main() {

 ...
 
    //增加一條記錄
    router.POST("/add", func(c *gin.Context) {
        name := c.Request.FormValue("name")
        telephone := c.Request.FormValue("telephone")
        person := Person{
            Name:name,
            Telephone:telephone,
        }
        id := person.Create()
        msg := fmt.Sprintf("insert successful %d", id)
        c.JSON(http.StatusOK, gin.H{
            "msg": msg,
        })
    })
 
 ...
}

//插入
func (person *Person) Create() int64 {
    rs, err := db.Exec("INSERT into users (name, telephone) value (?,?)", person.Name, person.Telephone)
    if err != nil{
        log.Fatal(err)
    }
    id, err := rs.LastInsertId()
    if err != nil{
        log.Fatal(err)
    }
    return id
}

func checkErr(err error)  {
    if err != nil{
        panic(err)
    }
}

執行非query操作,使用db的Exec方法,在mysql中使用?做占位符。最后我們把插入后的id返回給客戶端。請求得到的結果如下:

查詢列表 Query

上面我們增加了一條記錄,下面就獲取這個記錄,查一般有兩個操作,一個是查詢列表,其次就是查詢具體的某一條記錄。兩種大同小異。

為了給查詢結果綁定到golang的變量或對象,我們需要先定義一個結構來綁定對象。

router.GET("/users", func(c *gin.Context) {
    rs, _ := getRows()
    c.JSON(http.StatusOK, gin.H{
        "list": rs,
    })
})

//查詢所有記錄
func getRows() (persons []Person, err error) {
    rows, err := db.Query("select id,name,telephone from users")
    for rows.Next(){
        person := Person{}
        err := rows.Scan(&person.Id, &person.Name, &person.Telephone)
        if err != nil {
            log.Fatal(err)
        }
        persons = append(persons, person)
    }
    rows.Close()
    return
}

返回結果如下:

查詢單條記錄 QueryRow

查詢列表需要使用迭代rows對象,查詢單個記錄,就沒這么麻煩了。雖然也可以迭代一條記錄的結果集。因為查詢單個記錄的操作實在太常用了,因此golang的database/sql也專門提供了查詢方法

router.GET("/users/:id", func(c *gin.Context) {
        id_string := c.Param("id")
        id, _ := strconv.Atoi(id_string)
        rs, _ := getRow(id)
        c.JSON(http.StatusOK, gin.H{
            "result": rs,
        })
    })

//查詢一條記錄
func getRow(id int) (person Person, err error)  {
    person = Person{}
    err = db.QueryRow("select id,name,telephone from users where id = ?", id).Scan(&person.Id, &person.Name, &person.Telephone)
    return
}

返回結果如下:

增刪改查,下面進行更新的操作。

router.POST("/users/update", func(c *gin.Context) {
        ids := c.Request.FormValue("id")
        id, _ := strconv.Atoi(ids)
        telephone := c.Request.FormValue("telephone")
        person := Person{
            Id:id,
            Telephone:telephone,
        }
        row := person.Update()
        msg := fmt.Sprintf("updated successful %d", row)
        c.JSON(http.StatusOK, gin.H{
            "msg": msg,
        })
    })
/修改
func (person *Person) Update() int64{
    rs, err := db.Exec("update users set telephone = ? where id = ?", person.Telephone, person.Id)
    if err != nil {
        log.Fatal(err)
    }
    rows, err := rs.RowsAffected()
    if err != nil {
        log.Fatal(err)
    }
    return  rows
}

返回結果如下:

最后一個操作就是刪除了,刪除所需要的功能特性,上面的例子都覆蓋了。實現刪除也就特別簡單了:

//刪除一條記錄
    router.POST("/users/del", func(c *gin.Context) {
        ids := c.Request.FormValue("id")
        id, _ := strconv.Atoi(ids)
        row := Delete(id)
        msg := fmt.Sprintf("delete successful %d", row)
        c.JSON(http.StatusOK, gin.H{
            "msg": msg,
        })
    })

func Delete(id int) int64  {
    rs, err := db.Exec("delete from users where id = ?", id)
    if err != nil {
        log.Fatal()
    }
    rows, err := rs.RowsAffected()
    if err != nil {
        log.Fatal()
    }
    return rows
}

返回結果:

至此,基本的CURD操作的restful風格的API已經完成。內容其實不復雜,甚至相當簡單。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM