Go语言RESTful API开发实战

来源:互联网 发布:形势与政策论文 网络 编辑:程序博客网 时间:2024/04/17 01:28

如今微服务、无服务器架构大行其道。而API开发是这些话题的主角。
Go语言开发效率要比Java Spring要快一些,性能上比PHP高出一个数量级。尤其是Go语言在并发方便非常的优秀,是2017年值得关注的一门语言。
本文通过一个经典的Todo应用来介绍使用Go语言开发API。
配套的演示代码

主要涉及的内容:

  • API开发框架gin-gonic
  • ORM框架gorm
  • Go语言mysql驱动

依赖包

$ go get gopkg.in/gin-gonic/gin.v1$ go get -u github.com/jinzhu/gorm$ go get github.com/go-sql-driver/mysql

接口文档:

接口文档

一、Hello World

package mainimport "github.com/gin-gonic/gin"func main() {    r := gin.Default()    r.GET("/ping", func(c *gin.Context) {        c.JSON(200, gin.H{            "message": "pong",        })    })    r.Run() // listen and serve on 0.0.0.0:8080}

二、路由设计

package mainimport "github.com/gin-gonic/gin"var router = gin.Default()func init()  {    router := gin.Default()    v1 := router.Group("/api/v1/todos")    {        v1.POST("/", createTodo)        v1.GET("/", fetchAllTodo)        v1.GET("/:id", fetchSingleTodo)        v1.PUT("/:id", updateTodo)        v1.DELETE("/:id", deleteTodo)    }}

三、设计数据库

drop database if exists demo;create database demo charset='utf8';use demo;drop table if exists todo;create table todo (    primary key(id),    id int not null auto_increment,    title varchar(256) not null default '待办事项',    completed bool not null default 0,    created_at timestamp not null default current_timestamp,    updated_at timestamp not null default current_timestamp,    deleted_at timestamp not null default current_timestamp) Engine=Innodb;

四、连接数据库

package mainimport (    "github.com/jinzhu/gorm"    _ "github.com/jinzhu/gorm/dialects/mysql")var (    db *gorm.DB    sqlConnection = "golang:1234567890@(114.115.136.205)/demo?charset=utf8&parseTime=True&loc=Local")func init() {    //打开数据库连接    var err error    db, err = gorm.Open("mysql", sqlConnection)    if err != nil {        panic("failed to connect database")    }    db.AutoMigrate(&todoModel{})}

五、模型设计

package mainimport "github.com/jinzhu/gorm"type (    // entity类    todoModel struct {        gorm.Model        Title     string `json:"title"`        Completed int    `json:"completed"`    }    // response entity    transformedTodo struct {        ID        uint   `json:"id"`        Title     string `json:"title"`        Completed bool   `json:"completed"`    })

六、CRUD

package mainimport (    "github.com/gin-gonic/gin"    "strconv"    "net/http")// createTodo add a new todofunc createTodo(c *gin.Context) {    completed, _ := strconv.Atoi(c.PostForm("completed"))    todo := todoModel{Title: c.PostForm("title"), Completed: completed}    db.Save(&todo)    c.JSON(http.StatusCreated, gin.H{"status": http.StatusCreated, "message": "Todo item created successfully!", "resourceId": todo.ID})}// fetchAllTodo fetch all todosfunc fetchAllTodo(c *gin.Context) {    var todos []todoModel    var _todos []transformedTodo    db.Find(&todos)    if len(todos) <= 0 {        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})        return    }    //transforms the todos for building a good response    for _, item := range todos {        completed := false        if item.Completed == 1 {            completed = true        } else {            completed = false        }        _todos = append(_todos, transformedTodo{ID: item.ID, Title: item.Title, Completed: completed})    }    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": _todos})}// fetchSingleTodo fetch a single todofunc fetchSingleTodo(c *gin.Context) {    var todo todoModel    todoID := c.Param("id")    db.First(&todo, todoID)    if todo.ID == 0 {        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})        return    }    completed := false    if todo.Completed == 1 {        completed = true    } else {        completed = false    }    _todo := transformedTodo{ID: todo.ID, Title: todo.Title, Completed: completed}    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "data": _todo})}// updateTodo update a todofunc updateTodo(c *gin.Context) {    var todo todoModel    todoID := c.Param("id")    db.First(&todo, todoID)    if todo.ID == 0 {        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})        return    }    db.Model(&todo).Update("title", c.PostForm("title"))    completed, _ := strconv.Atoi(c.PostForm("completed"))    db.Model(&todo).Update("completed", completed)    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "Todo updated successfully!"})}// deleteTodo remove a todofunc deleteTodo(c *gin.Context) {    var todo todoModel    todoID := c.Param("id")    db.First(&todo, todoID)    if todo.ID == 0 {        c.JSON(http.StatusNotFound, gin.H{"status": http.StatusNotFound, "message": "No todo found!"})        return    }    db.Delete(&todo)    c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "Todo deleted successfully!"})}

七、编译打包

将多个go文件打包为一个可执行文件

go build main.go router.go controller.go config.go model.go

如果把所有的代码放在一个文件里面,直接运行即可go build main.go
为了方便展示,这里给出了已经打包好的文件

八、单元测试(略)

九、运维上线

nohup ./main &

十、效果展示

创建:
创建

获取:
获取

更新:
更新

删除:
删除

本文的原始项目是来自原文地址。
但是由于一些本土化的需要(不用翻墙),做了较大的改动。