欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API實(shí)戰(zhàn)示例詳解

 更新時(shí)間:2021年11月16日 14:58:32   作者:秋天的春  
這篇文章主要為大家介紹了Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API示例詳解,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進(jìn)步

我們已經(jīng)了解了Golang的Gin框架。對(duì)于Webservice服務(wù),restful風(fēng)格幾乎一統(tǒng)天下。Gin也天然的支持restful。下面就使用gin寫一個(gè)簡(jiǎn)單的服務(wù),麻雀雖小,五臟俱全。我們先以一個(gè)單文件開(kāi)始,然后再逐步分解模塊成包,組織代碼。

It works

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

參考Golang 微框架Gin簡(jiǎn)介Golang持久化。

創(chuàng)建一個(gè)文件夾用來(lái)為項(xiàng)目,新建一個(gè)文件main.go:

☁  newgin  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, "It works")
 }) 
 router.Run(":8000")
}

編譯運(yùn)行

☁  newgin  go run main.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env: export GIN_MODE=release
 - using code: gin.SetMode(gin.ReleaseMode) 
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :8000
 

訪問(wèn) /即可看見(jiàn)我們返回的字串It works

數(shù)據(jù)庫(kù)

安裝完畢框架,完成一次請(qǐng)求響應(yīng)之后。接下來(lái)就是安裝數(shù)據(jù)庫(kù)驅(qū)動(dòng)和初始化數(shù)據(jù)相關(guān)的操作了。首先,我們需要新建數(shù)據(jù)表。一個(gè)及其簡(jiǎn)單的數(shù)據(jù)表:

CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(40) NOT NULL DEFAULT '',
  `last_name` varchar(40) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

創(chuàng)建數(shù)據(jù)表之后,初始化數(shù)據(jù)庫(kù)連接池:

func main() { 
 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)
 } 
 router := gin.Default()
 router.GET("/", func(c *gin.Context) {
  c.String(http.StatusOK, "It works")
 }) 
 router.Run(":8000")
}

使用sql.Open方法會(huì)創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接池db。這個(gè)db不是數(shù)據(jù)庫(kù)連接,它是一個(gè)連接池,只有當(dāng)真正數(shù)據(jù)庫(kù)通信的時(shí)候才創(chuàng)建連接。例如這里的db.Ping的操作。db.SetMaxIdleConns(20)db.SetMaxOpenConns(20)分別設(shè)置數(shù)據(jù)庫(kù)的空閑連接和最大打開(kāi)連接,即向Mysql服務(wù)端發(fā)出的所有連接的最大數(shù)目。

如果不設(shè)置,默認(rèn)都是0,表示打開(kāi)的連接沒(méi)有限制。我在壓測(cè)的時(shí)候,發(fā)現(xiàn)會(huì)存在大量的TIME_WAIT狀態(tài)的連接,雖然mysql的連接數(shù)沒(méi)有上升。設(shè)置了這兩個(gè)參數(shù)之后,不在存在大量TIME_WAIT狀態(tài)的連接了。而且qps也沒(méi)有明顯的變化,出于對(duì)數(shù)據(jù)庫(kù)的保護(hù),最好設(shè)置這連個(gè)參數(shù)。

CURD 增刪改查

Restful的基本就是對(duì)資源的curd操作。下面開(kāi)啟我們的第一個(gè)api接口,增加一個(gè)資源。

func main() { 
 ... 
 router.POST("/person", func(c *gin.Context) {
  firstName := c.Request.FormValue("first_name")
  lastName := c.Request.FormValue("last_name") 
  rs, err := db.Exec("INSERT INTO person(first_name, last_name) VALUES (?, ?)", firstName, lastName)
  if err != nil {
   log.Fatalln(err)
  } 
  id, err := rs.LastInsertId()
  if err != nil {
   log.Fatalln(err)
  }
  fmt.Println("insert person Id {}", id)
  msg := fmt.Sprintf("insert successful %d", id)
  c.JSON(http.StatusOK, gin.H{
   "msg": msg,
  })
 }) 
 ...
}
 

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

☁  ~  curl -X POST http://127.0.0.1:8000/person -d "first_name=hello&last_name=world" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    62  100    30  100    32   5054   5391 --:--:-- --:--:-- --:--:--  6400
{
    "msg": "insert successful 1"
}

下面可以隨意增加幾條記錄。

查詢列表 Query

上面我們?cè)黾恿艘粭l記錄,下面就獲取這個(gè)記錄,查一般有兩個(gè)操作,一個(gè)是查詢列表,其次就是查詢具體的某一條記錄。兩種大同小異。

為了給查詢結(jié)果綁定到golang的變量或?qū)ο?,我們需要先定義一個(gè)結(jié)構(gòu)來(lái)綁定對(duì)象。在main函數(shù)的上方定義Person結(jié)構(gòu):

type Person struct {
 Id        int    `json:"id" form:"id"`
 FirstName string `json:"first_name" form:"first_name"`
 LastName  string `json:"last_name" form:"last_name"`
}

然后查詢我們的數(shù)據(jù)列表

 router.GET("/persons", func(c *gin.Context) {
  rows, err := db.Query("SELECT id, first_name, last_name FROM person")  
  if err != nil {
   log.Fatalln(err)
  }
 defer rows.Close()
   persons := make([]Person, 0)
  for rows.Next() {
   var person Person
   rows.Scan(&person.Id, &person.FirstName, &person.LastName)
   persons = append(persons, person)
  }
  if err = rows.Err(); err != nil {
   log.Fatalln(err)
  } 
  c.JSON(http.StatusOK, gin.H{
   "persons": persons,
  }) 
 })

讀取mysql的數(shù)據(jù)需要有一個(gè)綁定的過(guò)程,db.Query方法返回一個(gè)rows對(duì)象,這個(gè)數(shù)據(jù)庫(kù)連接隨即也轉(zhuǎn)移到這個(gè)對(duì)象,因此我們需要定義row.Close操作。然后創(chuàng)建一個(gè)[]Person的切片。

使用make,而不是直接使用var persons []Person的聲明方式。還是有所差別的,使用make的方式,當(dāng)數(shù)組切片沒(méi)有元素的時(shí)候,Json會(huì)返回[]。如果直接聲明,json會(huì)返回null

接下來(lái)就是使用rows對(duì)象的Next方法,遍歷所查詢的數(shù)據(jù),一個(gè)個(gè)綁定到person對(duì)象上,最后append到persons切片。

☁  ~  curl  http://127.0.0.1:8000/persons | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   113  100   113    0     0   101k      0 --:--:-- --:--:-- --:--:--  110k
{
    "persons": [
        {
            "first_name": "hello",
            "id": 1,
            "last_name": "world"
        },
        {
            "first_name": "vanyar",
            "id": 2,
            "last_name": "elves"
        }
    ]
}

查詢單條記錄 QueryRow

查詢列表需要使用迭代rows對(duì)象,查詢單個(gè)記錄,就沒(méi)這么麻煩了。雖然也可以迭代一條記錄的結(jié)果集。因?yàn)椴樵儐蝹€(gè)記錄的操作實(shí)在太常用了,因此golang的database/sql也專門提供了查詢方法

 router.GET("/person/:id", func(c *gin.Context) {
  id := c.Param("id")
  var person Person
  err := db.QueryRow("SELECT id, first_name, last_name FROM person WHERE id=?", id).Scan(
   &person.Id, &person.FirstName, &person.LastName,
  )
  if err != nil {
   log.Println(err)
   c.JSON(http.StatusOK, gin.H{
    "person": nil,
   })
   return
  }
   c.JSON(http.StatusOK, gin.H{
   "person": person,
  })
 
 })

查詢結(jié)果為:

☁  ~  curl  http://127.0.0.1:8000/person/1 | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    60  100    60    0     0  20826      0 --:--:-- --:--:-- --:--:-- 30000
{
    "person": {
        "first_name": "hello",
        "id": 1,
        "first_name": "world"
    }
}

查詢單個(gè)記錄有一個(gè)小問(wèn)題,當(dāng)數(shù)據(jù)不存在的時(shí)候,同樣也會(huì)拋出一個(gè)錯(cuò)誤。粗暴的使用log退出有點(diǎn)不妥。返回一個(gè)nil的時(shí)候,萬(wàn)一真的是因?yàn)殄e(cuò)誤,比如sql錯(cuò)誤。這種情況如何解決。還需要具體場(chǎng)景設(shè)計(jì)程序。

增刪改查,下面進(jìn)行更新的操作。前面增加記錄我們使用了urlencode的方式提交,更新的api我們自動(dòng)匹配綁定content-type

 router.PUT("/person/:id", func(c *gin.Context) {
  cid := c.Param("id")
  id, err := strconv.Atoi(cid)
  person := Person{Id: id}
  err = c.Bind(&person)
  if err != nil {
   log.Fatalln(err)
  }
  stmt, err := db.Prepare("UPDATE person SET first_name=?, last_name=? WHERE id=?")
  defer stmt.Close()
  if err != nil {
   log.Fatalln(err)
  }
  rs, err := stmt.Exec(person.FirstName, person.LastName, person.Id)
  if err != nil {
   log.Fatalln(err)
  }
  ra, err := rs.RowsAffected()
  if err != nil {
   log.Fatalln(err)
  }
  msg := fmt.Sprintf("Update person %d successful %d", person.Id, ra)
  c.JSON(http.StatusOK, gin.H{
   "msg": msg,
  })
 })
 

使用 urlencode的方式更新:

☁  ~  curl -X PUT http://127.0.0.1:8000/person/2 -d "first_name=noldor&last_name=elves" | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    72  100    39  100    33   3921   3317 --:--:-- --:--:-- --:--:--  4333
{
    "msg": "Update person 2 successful 1"
}

使用json的方式更新:

☁  ~  curl -X PUT http://127.0.0.1:8000/person/2 -H "Content-Type: application/json"  -d '{"first_name": "vanyar", "last_name": "elves"}' | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    85  100    39  100    46   4306   5079 --:--:-- --:--:-- --:--:--  5750
{
    "msg": "Update person 2 successful 1"
}

最后一個(gè)操作就是刪除了,刪除所需要的功能特性,上面的例子都覆蓋了。實(shí)現(xiàn)刪除也就特別簡(jiǎn)單了:

 router.DELETE("/person/:id", func(c *gin.Context) {
  cid := c.Param("id")
  id, err := strconv.Atoi(cid)
  if err != nil {
   log.Fatalln(err)
  }
  rs, err := db.Exec("DELETE FROM person WHERE id=?", id)
  if err != nil {
   log.Fatalln(err)
  }
  ra, err := rs.RowsAffected()
  if err != nil {
   log.Fatalln(err)
  }
  msg := fmt.Sprintf("Delete person %d successful %d", id, ra)
  c.JSON(http.StatusOK, gin.H{
   "msg": msg,
  })
 })

我們可以使用刪除接口,把數(shù)據(jù)都刪除了,再來(lái)驗(yàn)證上面post接口獲取列表的時(shí)候,當(dāng)記錄沒(méi)有的時(shí)候,切片被json序列化[]還是null

☁  ~  curl  http://127.0.0.1:8000/persons | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    15  100    15    0     0  11363      0 --:--:-- --:--:-- --:--:-- 15000
{
    "persons": []
}

persons := make([]Person, 0)改成persons []Person。編譯運(yùn)行:

☁  ~  curl  http://127.0.0.1:8000/persons | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    17  100    17    0     0  13086      0 --:--:-- --:--:-- --:--:-- 17000
{
    "persons": null
}

至此,基本的CURD操作的restful風(fēng)格的API已經(jīng)完成。內(nèi)容其實(shí)不復(fù)雜,甚至相當(dāng)簡(jiǎn)單。完整的代碼可以通過(guò)GIST獲取。

組織代碼

實(shí)現(xiàn)了一個(gè)基本點(diǎn)restful服務(wù),可惜我們的代碼都在一個(gè)文件中。對(duì)于一個(gè)庫(kù),單文件或許很好,對(duì)于稍微大一點(diǎn)的項(xiàng)目,單文件總是有點(diǎn)非主流。當(dāng)然,更多原因是為了程序的可讀和維護(hù),我們也需要重新組織代碼,拆分模塊和包。

封裝模型方法

我們的handler出來(lái)函數(shù)中,對(duì)請(qǐng)求的出來(lái)和數(shù)據(jù)庫(kù)的交互,都糅合在一起。首先我們基于創(chuàng)建的Person結(jié)構(gòu)創(chuàng)建數(shù)據(jù)模型,以及模型的方法。把數(shù)據(jù)庫(kù)交互拆分出來(lái)。

創(chuàng)建一個(gè)單例的數(shù)據(jù)庫(kù)連接池對(duì)象:

var db *sql.DB
func main() {
 var err error
 db, err = sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
 if err != nil {
  log.Fatalln(err)
 } 
 defer db.Close()
 if err := db.Ping(); err != nil {
  log.Fatalln(err)
 }
 ...
}

這樣在main包中,db就能隨意使用了。

接下來(lái),再把增加記錄的的函數(shù)封裝成Person結(jié)構(gòu)的方法:

func (p *Person) AddPerson() (id int64, err error) {
 rs, err := db.Exec("INSERTs INTO person(first_name, last_name) VALUES (?, ?)", p.FirstName, p.LastName)
 if err != nil {
  return
 }
 id, err = rs.LastInsertId()
 return
}

然后handler函數(shù)也跟著修改,先創(chuàng)建一個(gè)Person結(jié)構(gòu)的實(shí)例,然后調(diào)用其方法即可:

 router.POST("/person", func(c *gin.Context) {
  firstName := c.Request.FormValue("first_name")
  lastName := c.Request.FormValue("last_name") 
  person := Person{FirstName: firstName, LastName: lastName}
  ra_rows, err := person.AddPerson()
  if err != nil {
   log.Fatalln(err)
  }
  msg := fmt.Sprintf("insert successful %d", ra_rows)
  c.JSON(http.StatusOK, gin.H{
   "msg": msg,
  })
 })

對(duì)于獲取列表的模型方法和handler函數(shù)也很好改:

func (p *Person) GetPersons() (persons []Person, err error) {
 persons = make([]Person, 0)
 rows, err := db.Query("SELECT id, first_name, last_name FROM person")
 defer rows.Close() 
 if err != nil {
  return
 } 
 for rows.Next() {
  var person Person
  rows.Scan(&person.Id, &person.FirstName, &person.LastName)
  persons = append(persons, person)
 }
 if err = rows.Err(); err != nil {
  return
 }
 return
}

 router.POST("/person", func(c *gin.Context) {
  firstName := c.Request.FormValue("first_name")
  lastName := c.Request.FormValue("last_name")
  person := Person{FirstName: firstName, LastName: lastName}
  ra_rows, err := person.AddPerson()
  if err != nil {
   log.Fatalln(err)
  }
  msg := fmt.Sprintf("insert successful %d", ra_rows)
  c.JSON(http.StatusOK, gin.H{
   "msg": msg,
  })
 })

剩下的函數(shù)和方法就不再一一舉例了。

增加記錄的接口中,我們使用了客戶端參數(shù)和Person創(chuàng)建實(shí)例,然后再調(diào)用其方法。而獲取列表的接口中,我們直接聲明了Person對(duì)象。兩種方式都可以。

Handler函數(shù)

gin提供了router.Get(url, handler func)的格式。首先我們可以把所有的handler函數(shù)從router中提取出來(lái)。

例如把增加記錄和獲取列表的handle提取出來(lái)

func AddPersonApi(c *gin.Context) {
 firstName := c.Request.FormValue("first_name")
 lastName := c.Request.FormValue("last_name") 
 person := Person{FirstName: firstName, LastName: lastName} 
 ra_rows, err := person.AddPerson()
 if err != nil {
  log.Fatalln(err)
 }
 msg := fmt.Sprintf("insert successful %d", ra_rows)
 c.JSON(http.StatusOK, gin.H{
  "msg": msg,
 })
} 
func main(){
 ...
 router.POST("/person", AddPersonApi)
 ... 
}

把modle和handler抽出來(lái)之后,我們的代碼結(jié)構(gòu)變得更加清晰,具體可以參考這個(gè)GIST

組織項(xiàng)目

經(jīng)過(guò)上面的model和handler的分離,代碼結(jié)構(gòu)變得更加清晰,可是我們還是單文件。下一步將進(jìn)行封裝不同的包。

數(shù)據(jù)庫(kù)處理

在項(xiàng)目根目錄創(chuàng)建下面三個(gè)文件夾,apis,databasesmodels,并在文件夾內(nèi)創(chuàng)建文件。此時(shí)我們的目錄結(jié)果如下:

apis文件夾存放我們的handler函數(shù),models文件夾用來(lái)存放我們的數(shù)據(jù)模型。

myql.go的包代碼如下:

package database 
import (
 "database/sql"
 _ "github.com/go-sql-driver/mysql"
 "log"
) 
var SqlDB *sql.DB 
func init() {
 var err error
 SqlDB, err = sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test?parseTime=true")
 if err != nil {
  log.Fatal(err.Error())
 }
 err = SqlDB.Ping()
 if err != nil {
  log.Fatal(err.Error())
 }
}

因?yàn)槲覀冃枰趧e的地方使用SqlDB這個(gè)變量,因此依照golang的習(xí)慣,變量名必須大寫開(kāi)頭。

數(shù)據(jù)model封裝

修改models文件夾下的person.go,把對(duì)應(yīng)的Person結(jié)構(gòu)及其方法移到這里:

package models 
import (
 "log"
 db "newgin/database"
) 
type Person struct {
 Id        int    `json:"id" form:"id"`
 FirstName string `json:"first_name" form:"first_name"`
 LastName  string `json:"last_name" form:"last_name"`
} 
func (p *Person) AddPerson() (id int64, err error) {
 rs, err := db.SqlDB.Exec("INSERT INTO person(first_name, last_name) VALUES (?, ?)", p.FirstName, p.LastName)
 if err != nil {
  return
 }
 id, err = rs.LastInsertId()
 return
} 
func (p *Person) GetPersons() (persons []Person, err error) {
 persons = make([]Person, 0)
 rows, err := db.SqlDB.Query("SELECT id, first_name, last_name FROM person")
 defer rows.Close() 
 if err != nil {
  return
 } 
 for rows.Next() {
  var person Person
  rows.Scan(&person.Id, &person.FirstName, &person.LastName)
  persons = append(persons, person)
 }
 if err = rows.Err(); err != nil {
  return
 }
 return
}
....
 

handler

然后把具體的handler函數(shù)封裝到api包中,因?yàn)閔andler函數(shù)要操作數(shù)據(jù)庫(kù),所以會(huì)引用model包

package apis 
import (
 "net/http"
 "log"
 "fmt"
 "strconv"
 "gopkg.in/gin-gonic/gin.v1"
 . "newgin/models"
) 
func IndexApi(c *gin.Context) {
 c.String(http.StatusOK, "It works")
} 
func AddPersonApi(c *gin.Context) {
 firstName := c.Request.FormValue("first_name")
 lastName := c.Request.FormValue("last_name") 
 p := Person{FirstName: firstName, LastName: lastName} 
 ra, err := p.AddPerson()
 if err != nil {
  log.Fatalln(err)
 }
 msg := fmt.Sprintf("insert successful %d", ra)
 c.JSON(http.StatusOK, gin.H{
  "msg": msg,
 })
}
...

路由

最后就是把路由抽離出來(lái),修改router.go,我們?cè)诼酚晌募蟹庋b路由函數(shù)

package main
import (
 "gopkg.in/gin-gonic/gin.v1"
 . "newgin/apis"
) 
func initRouter() *gin.Engine {
 router := gin.Default() 
 router.GET("/", IndexApi) 
 router.POST("/person", AddPersonApi) 
 router.GET("/persons", GetPersonsApi)
 router.GET("/person/:id", GetPersonApi)
 router.PUT("/person/:id", ModPersonApi) 
 router.DELETE("/person/:id", DelPersonApi) 
 return router
}
 

分組路由

	v1 := router.Group("/v1").Use(middleware.AuthRequired())
	{
		v1.GET("/", IndexApi)
 
		v1.GET("/person", AddPersonApi)
 
		v1.GET("/persons", GetPersonsApi)
 
		v1.POST("/person/:id", GetPersonApi)
		//
		v1.PUT("/person/:id", EditPersonApi)
		//
		v1.DELETE("/person/:id", DelPersonApi)
	}

app入口

最后就是main函數(shù)的app入口,將路由導(dǎo)入,同時(shí)我們要在main函數(shù)結(jié)束的時(shí)候,關(guān)閉全局的數(shù)據(jù)庫(kù)連接池:

main.go

package main 
import (
 db "newgin/database"
) 
func main() {
 defer db.SqlDB.Close()
 router := initRouter()
 router.Run(":8000")
}
 

至此,我們就把簡(jiǎn)單程序進(jìn)行了更好的組織。當(dāng)然,golang的程序組織依包為基礎(chǔ),不拘泥,根據(jù)具體的應(yīng)用場(chǎng)景可以組織。

此時(shí)運(yùn)行項(xiàng)目,不能像之前簡(jiǎn)單的使用go run main.go,因?yàn)榘黰ain包含main.go和router.go的文件,因此需要運(yùn)行go run *.go命令編譯運(yùn)行。如果是最終編譯二進(jìn)制項(xiàng)目,則運(yùn)行go build -o app

總結(jié)

通過(guò)上述的實(shí)踐,我們了解了Gin框架創(chuàng)建基本的的restful服務(wù)。并且了解了如何組織golang的代碼包。我們討論了很多內(nèi)容,但是唯獨(dú)缺少測(cè)試。測(cè)試很重要,考察一個(gè)框架或者三方包的時(shí)候,是否有測(cè)試文件以及測(cè)試覆蓋率是一個(gè)重要的參考。因?yàn)闇y(cè)試的內(nèi)容很多,我們這里就不做單獨(dú)的測(cè)試介紹。后面會(huì)結(jié)合gofight給gin的api增加測(cè)試代碼。

此外,更多的內(nèi)容,可以閱讀別人優(yōu)秀的開(kāi)源項(xiàng)目,學(xué)習(xí)并實(shí)踐,以提升自己的編碼能力。

以上就是Gin與Mysql實(shí)現(xiàn)簡(jiǎn)單Restful風(fēng)格API示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Gin與Mysql實(shí)現(xiàn)Restful風(fēng)格API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Win10系統(tǒng)下Golang環(huán)境搭建全過(guò)程

    Win10系統(tǒng)下Golang環(huán)境搭建全過(guò)程

    在編程語(yǔ)言的選取上,越來(lái)越多的人選擇了Golang,下面這篇文章主要給大家介紹了關(guān)于Win10系統(tǒng)下Golang環(huán)境搭建的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • golang 如何用反射reflect操作結(jié)構(gòu)體

    golang 如何用反射reflect操作結(jié)構(gòu)體

    這篇文章主要介紹了golang 用反射reflect操作結(jié)構(gòu)體的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • 解析Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)

    解析Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)

    http.FileServer 方法屬于標(biāo)準(zhǔn)庫(kù) net/http,返回一個(gè)使用 FileSystem 接口 root 提供文件訪問(wèn)服務(wù)的 HTTP 處理器。下面通過(guò)本文給大家介紹Go 標(biāo)準(zhǔn)庫(kù) http.FileServer 實(shí)現(xiàn)靜態(tài)文件服務(wù)的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2018-08-08
  • 詳解Golang中的交叉編譯

    詳解Golang中的交叉編譯

    在 Golang 中,交叉編譯指的是在同一臺(tái)機(jī)器上生成針對(duì)不同操作系統(tǒng)或硬件架構(gòu)的二進(jìn)制文件,這在開(kāi)發(fā)跨平臺(tái)應(yīng)用或構(gòu)建特定平臺(tái)的發(fā)布版本時(shí)非常有用,本文就詳細(xì)的給大家介紹一下Golang中的交叉編譯,需要的朋友可以參考下
    2023-08-08
  • golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換

    golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換

    本文主要介紹了golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • golang方法中receiver為指針與不為指針的區(qū)別詳析

    golang方法中receiver為指針與不為指針的區(qū)別詳析

    這篇文章主要給大家介紹了關(guān)于golang方法中receiver為指針與不為指針區(qū)別的相關(guān)資料,其實(shí)最大的區(qū)別應(yīng)該是指針傳遞的是對(duì)像的引用,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-10-10
  • GO使用socket和channel實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)聊天室

    GO使用socket和channel實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)聊天室

    今天小編給大家分享一個(gè)簡(jiǎn)單的聊天室功能,聊天室主要功能是用戶可以加入離開(kāi)聊天室,實(shí)現(xiàn)思路也很簡(jiǎn)單明了,下面小編給大家?guī)?lái)了完整代碼,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Golang map與sync.map的異同詳解

    Golang map與sync.map的異同詳解

    在Go語(yǔ)言中,map和sync.Map都是用于存儲(chǔ)鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu),但它們?cè)诓l(fā)安全性、性能和使用場(chǎng)景上存在顯著差異,接下來(lái)將深入探討這兩種數(shù)據(jù)結(jié)構(gòu)的異同,感興趣的朋友可以參考下
    2024-01-01
  • 詳解Go語(yǔ)言如何實(shí)現(xiàn)類似Python中的with上下文管理器

    詳解Go語(yǔ)言如何實(shí)現(xiàn)類似Python中的with上下文管理器

    熟悉?Python?的同學(xué)應(yīng)該知道?Python?中的上下文管理器非常好用,那么在?Go?中是否也能實(shí)現(xiàn)上下文管理器呢,下面小編就來(lái)和大家仔細(xì)講講吧
    2023-07-07
  • GoFrame框架gcache的緩存控制淘汰策略實(shí)踐示例

    GoFrame框架gcache的緩存控制淘汰策略實(shí)踐示例

    這篇文章主要為大家介紹了GoFrame框架gcache的緩存控制淘汰策略的實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論