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