• 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已经完成。内容其实不复杂,甚至相当简单。

  • 相关阅读:
    C#基础
    自动化测试
    C# 数据结构题目
    .NET基础知识
    Sharepoint题目
    题目总结2
    数据库索引
    题目总结(2014-1-10)
    Stack详解
    SpringBoot入门基础知识点
  • 原文地址:https://www.cnblogs.com/liuzhongchao/p/9244516.html
Copyright © 2020-2023  润新知