• 基于gin web框架搭建RESTful API服务


     

    这篇主要学习go项目中的项目结构、项目规范等知识,ROM采用的database/sql的写法。

    1.技术框架

    利用的是ginweb框架,然后ROM层选用database/sql,安装mysql驱动。安装方式如下:

    //使用github上的gin托管地址
    $ go get -u github.com/gin-gonic/gin
    $ go get github.com/go-sql-driver/mysql
    

    2.项目结构如下

    项目结构分析:

    • 1、main.go主要是存放路由,启动项目;
    • 2、router主要存放路由信息,然后返回一个router
    • 3、apis存放routerHandler函数;
    • 4、databases存放数据连接信息;
    • 5、models存放数据模型,类似Java中POJO对象。
    │  main.go
    │
    ├─.idea
    │  │  go.iml
    │  │  misc.xml
    │  │  modules.xml
    │  │  workspace.xml
    │  │
    │  └─inspectionProfiles
    ├─apis
    │      person.go
    │
    ├─databases
    │      mysql.go
    │
    ├─models
    │      person.go
    │
    └─router
            router.go
    
    image.png

    3.main.go代码解释

    package main
    import (
        //这里讲db作为go/databases的一个别名,表示数据库连接池
        db "go/databases"
        . "go/router"
    )
    func main() {
        //当整个程序完成之后关闭数据库连接
        defer db.SqlDB.Close()
        router := InitRouter()
        router.Run(":8080")
    }
    

    4.router.go代码解释

    package router
    import (
        "github.com/gin-gonic/gin"
        ."go/apis"
    )
    func InitRouter() *gin.Engine {
        router := gin.Default()
        //IndexApi为一个Handler
        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
    }
    

    5.mysql.go代码解释

    package databases
    import (
        "database/sql"
        _ "github.com/go-sql-driver/mysql"
        "log"
    )
    //因为我们需要在其他地方使用SqlDB这个变量,所以需要大写代表public
    var SqlDB *sql.DB
    //初始化方法
    func init() {
        var err error
        SqlDB, err = sql.Open("mysql", "root:123456@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())
        }
    }
    

    使用sql.Open()方法会创建一个数据库连接池db。这个地步不是数据库连接,它是一个连接池,只有当真正的数据库通信的时候才创建连接。例如,这里的db.Ping()操作。db.SetMaxIdleConns(20)db.SetMaxOpenConns(20)分别设置数据库的空闲连接和最大打开连接,即向Mysql服务端发出的所有连接的最大数目。

    6.models中person.go代码解释

    package models
    import (
       "log"
       db "go/databases"
    )
    //定义person类型结构
    type Person struct {
       Id        int    `json:"id"`
       FirstName string `json:"first_name"`
       LastName  string `json:"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
    }
    
    func (p *Person) GetPerson() (person Person, err error) {
       err = db.SqlDB.QueryRow("SELECT id, first_name, last_name FROM person WHERE id=?", p.Id).Scan(
          &person.Id, &person.FirstName, &person.LastName,
       )
       return
    }
    
    func (p *Person) ModPerson() (ra int64, err error) {
       stmt, err := db.SqlDB.Prepare("UPDATE person SET first_name=?, last_name=? WHERE id=?")
       defer stmt.Close()
       if err != nil {
          return
       }
       rs, err := stmt.Exec(p.FirstName, p.LastName, p.Id)
       if err != nil {
          return
       }
       ra, err = rs.RowsAffected()
       return
    }
    
    func (p *Person) DelPerson() (ra int64, err error) {
       rs, err := db.SqlDB.Exec("DELETE FROM person WHERE id=?", p.Id)
       if err != nil {
          log.Fatalln(err)
       }
       ra, err = rs.RowsAffected()
       return
    }
    
    

    执行非query操作,使用dbExec方法,在MySQL中使用做占位符。最后我们把插入后的Id返回给客户端。

    GetPersons方法解释:

    读取MySQL的数据需要有一个绑定的过程,db.Query()方法返回一个rows对象,这个数据库连接随即转移到这个对象,因此我们需要定义rows.Close()操作,然后创建一个[]Person的切片。

    使用make,而不是直接使用var persons []Person的声明方式。还是有所差别的,使用make的方式,当数组切片没有元素的时候,Json会返回[]。如果直接声明,json会返回null

    接下来就是使用rows对象的Next()方法,遍历所查询的数据,一个个绑定到person对象上,最后appendperson切片。

    7.apis中的person.go代码解释

    package apis
    
    import (
        "net/http"
        "log"
        "fmt"
        "strconv"
        "github.com/gin-gonic/gin"
         ."go/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,
        })
    }
    
    func GetPersonsApi(c *gin.Context) {
        var p Person
        persons, err := p.GetPersons()
        if err != nil {
            log.Fatalln(err)
        }
    
        c.JSON(http.StatusOK, gin.H{
            "persons": persons,
        })
    
    }
    
    func GetPersonApi(c *gin.Context) {
        cid := c.Param("id")
        id, err := strconv.Atoi(cid)
        if err != nil {
            log.Fatalln(err)
        }
        p := Person{Id: id}
        person, err := p.GetPerson()
        if err != nil {
            log.Fatalln(err)
        }
    
        c.JSON(http.StatusOK, gin.H{
            "person": person,
        })
    
    }
    
    func ModPersonApi(c *gin.Context) {
        cid := c.Param("id")
        id, err := strconv.Atoi(cid)
        if err != nil {
            log.Fatalln(err)
        }
        p := Person{Id: id}
        err = c.Bind(&p)
        if err != nil {
            log.Fatalln(err)
        }
        ra, err := p.ModPerson()
        if err != nil {
            log.Fatalln(err)
        }
        msg := fmt.Sprintf("Update person %d successful %d", p.Id, ra)
        c.JSON(http.StatusOK, gin.H{
            "msg": msg,
        })
    }
    
    func DelPersonApi(c *gin.Context) {
        cid := c.Param("id")
        id, err := strconv.Atoi(cid)
        if err != nil {
            log.Fatalln(err)
        }
        p := Person{Id: id}
        ra, err := p.DelPerson()
        if err != nil {
            log.Fatalln(err)
        }
        msg := fmt.Sprintf("Delete person %d successful %d", id, ra)
        c.JSON(http.StatusOK, gin.H{
            "msg": msg,
        })
    }
    

    其实,整个项目的结构和CRUD操作跟Java中的思想比较类似,应该很容易上手。需要注意一点的是,如果需要将整个项目运行起来,项目的路径一定Gopath路径:F:GoProjectsrc;

    image.png
    image.png

    项目启动结果如下:

    image.png

    熟悉了database/sql的写法后,下一步就是学习ROM框架gorm的写法,进而学习Docker进行部署。

  • 相关阅读:
    linux下的crontab定时执行任务命令详解
    494. Target Sum
    347. Top K Frequent Elements
    5. Longest Palindromic Substring
    时间复杂度计算方法
    62. Unique Paths
    54. Spiral Matrix && 59. Spiral Matrix II
    57. Insert Interval
    53. Maximum Subarray
    42.Trapping rain water
  • 原文地址:https://www.cnblogs.com/fengff/p/11448047.html
Copyright © 2020-2023  润新知