Go语言

两种企业型语言, 一种是Java 另一种就是 Go语言,Java 和 Go 虽然都能实现并发,但是从底层而言,Java 作为上世纪90年代诞生的语言,并不是原生支持高并发,而Go语言不同,在2005年时, 出现了2核CPU,而Go语言则发布在2009 年,Go语言的设计者有三位,其中就包括C语言之父, 而Go 的语法也是类C的,对于一定编程基础的人来讲十分容易上手。

gin框架

与python 不同的是, Go语言的框架很杂,之前看到过一篇文章,将web服务框架分为两类,一种是较大的框架,包含的功能很广,实际开发时的代码量比较少,如beego;另一种是则微框架,功能包含基本的功能,实现复杂功能时需要写很多代码,如 gin echo。关于这两种框架,一些人认为类似beego 的框架太过于笨重,违背了go 语言本身轻量化的思想,我们使用go语言的原因之一就是避免 java 的笨重。 总之,我认为这种说法再某一方面是正确的,因此我选用了 github 上 star 比较多的 gin 框架。

实现

这里我选用的是 restful 风格的API, gin 对几类方法都有简单封装

router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)

初始化服务也很简单

func main() {
    // Disable Console Color
    // gin.DisableConsoleColor()

    // 使用默认中间件创建一个gin路由器
    // logger and recovery (crash-free) 中间件
    router := gin.Default()

    router.GET("/someGet", getting)
    router.POST("/somePost", posting)
    router.PUT("/somePut", putting)
    router.DELETE("/someDelete", deleting)
    router.PATCH("/somePatch", patching)
    router.HEAD("/someHead", head)
    router.OPTIONS("/someOptions", options)

    // 默认启动的是 8080端口,也可以自己定义启动端口
    router.Run()
    // router.Run(":3000") for a hard coded port
}

即在main 函数里定义router 即可

一个get请求一般这样定义

router.GET("/someGet", func(c *gin.Context) {
   // some code ...
   // ...
})

总感觉有些像 axios 处理ajax请求… 前后端果然都是相通的…

项目结构

总之,项目结构是这样的:

├─apis			// api函数的包,controler
├─databases		// 链接数据库
├─models		// 模型及函数
├─router		// 路由处理
├gin.log		// 日志文件
├main.go		// main文件

MVVM

MVVM ,当然要从M 即model 入手。

在model 包里定义的主要是model 数据模型、处理数据。

C语言的结构体定义:

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
}

当然,这里需要引入db 包,选用的数据库是 mysql,但由于go 的引库机制是同一库下的文件是相通的。我们只需要在 databases 包里面引入处理库

import ( // (databases包中引入) (其实这部分编辑器直接处理了)
"database/sql"
_ "github.com/go-sql-driver/mysql"
)

以及链接数据库:

func init() {
	var err error
	SqlDB, err = sql.Open("mysql", "gone:123456@tcp(127.0.0.1:3306)/gone?parseTime=true")
	if err != nil {
		log.Fatal(err.Error())
	}
	//连接检测
	err = SqlDB.Ping()
	if err != nil {
		log.Fatal(err.Error())
	}
}

有简单编程经验的同学很容易能看懂…

在 AddPerson 函数中, 返回的是一个 Person 类型的数据,我们需要在 apis 包中使用这些函数。

func AddPersonApi(c *gin.Context) {
	firstName := c.Request.FormValue("first_name") // 这里首先定义两个变量
	lastName := c.Request.FormValue("last_name")

	p := Person{FirstName: firstName, LastName: lastName} // 定义 Person 数据

	ra, err := p.AddPerson() // 这里用到model 中 addperson函数
	if err != nil {  // 错误处理
		log.Fatalln(err)
	}
	msg := fmt.Sprintf("insert successful %d", ra)
	c.JSON(http.StatusOK, gin.H{ // 返回数据
		"msg": msg,
	})
}

最后是router 包:

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
}

这里是之前提到过的函数…

最后就是 mian 函数:

func main() {
	f, _ := os.Create("gin.log") // 日志
	gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
	
	defer db.SqlDB.Close()	//当整个程序完成之后关闭数据库连接
	router := InitRouter()
	router.Run() // 默认 8080端口
}

数据库

1.以 root 登陆 mysql

mysql -uroot -p

2.创建 gone 用户 ‘gone’

create user 'gone'@'%' identified by '密码';

3.创建 gone 数据库

create database gone;

4.允许gone 用户访问权限

grant all privileges on gone.* to 'gone'@'%';

源代码

apis

package apis

import (
	"fmt"
	"github.com/gin-gonic/gin"
	. "gone/models"
	"log"
	"net/http"
	"strconv"
)

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,
	})
}

databases

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", "gone:123456@tcp(127.0.0.1:3306)/gone?parseTime=true")
	if err != nil {
		log.Fatal(err.Error())
	}
	//连接检测
	err = SqlDB.Ping()
	if err != nil {
		log.Fatal(err.Error())
	}
}

models

package models

import (
	db "gone/databases"
	"log"
)

//定义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
}

routers

package router

import (
	"github.com/gin-gonic/gin"
	."gone/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
}

main.go

package main

import (
	"github.com/gin-gonic/gin"
	"io"

	//这里讲db作为go/databases的一个别名,表示数据库连接池
	db "gone/databases"
	. "gone/router"
	"os"
)

func main() {
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

	//当整个程序完成之后关闭数据库连接
	defer db.SqlDB.Close()
	router := InitRouter()
	router.Run(":8080")
}