回顾

上一期上文中我们讲解了

  • gin的中间件的使用
  • post和get接口不同的获取参数方式
  • ...

这一期我们的主要目标是

  • 如何使用gin构建一个标准的web工程
  • 如何使用gin写出标准的api接口

同样的我们接着上一期的工程继续

工程地址server

查看一下目录

➜  go-gin-test tree -L 3 
.
├── go.mod
├── go.sum
├── mian.go
└── routerex
    └── router.go
复制代码

Web工程

目标:

MVC行为业务

目录创建

我们会创建几个目录

  • api 用来写web接口的目录
  • service 用来写业务逻辑的目录
  • model 用来存放对象结构体的目录
  • server 用来服务管理

查看一下目录

➜  go-gin-test git:(main) tree -L 3 
.
├── api
├── go.mod
├── go.sum
├── mian.go
├── model
├── server
├── routerex
│   ├── mid
│   │   └── mid.go
│   └── router.go
└── service
复制代码
mid.gorouter.go
跨域中间件
mid.go
package mid

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func MidCors(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", c.Request.Header.Get("Origin"))
	c.Header("Access-Control-Allow-Headers", "*")
	c.Header("Access-Control-Allow-Methods", "*")
	c.Header("Access-Control-Allow-Credentials", "true")

	//放行所有OPTIONS方法
	if c.Request.Method == "OPTIONS" {
		c.AbortWithStatus(http.StatusNoContent)
	}
	// 处理请求
	c.Next()
}

复制代码
router.go

package routerex

import (
	"example.com/m/v2/routerex/mid"
	"github.com/gin-gonic/gin"
)
//用作注册请求地址
func RegRouter(g *gin.Engine) {
	g1 := g.Group("/api")
	//中间件
	g1.Use(mid.MidCors)
}

复制代码
serverserver.go
server.go
package server

import (
	"example.com/m/v2/routerex"
	"github.com/gin-gonic/gin"
)

func ServerStart(port string) {
	r := gin.Default()
	routerex.RegRouter(r)
	r.Run(":" + port) // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

复制代码
main.go
package main

import "example.com/m/v2/server"
//在main文件中我们尽量只有一个函数和一个调用,避免一些不必要坑,如果有任何需要请写到server里面去
func main() {
	server.ServerStart("8080")
}


复制代码

到了这一步,实际上一个大概的框架轮廓已经出现

➜  go-gin-test git:(main) tree -L 3 
.
├── api    web接口
├── go.mod
├── go.sum
├── mian.go  程序入口
├── model   数据库对应的结构体
├── server  管理服务
├── routerex 路由
│   ├── mid
│   │   └── mid.go
│   └── router.go
└── service 业务逻辑
复制代码

数据库链接

数据库链接可以单独新建一个目录来管理

xormgo-sql-driver/mysql

运行以下命令导入

go get github.com/go-sql-driver/mysql
go get xorm.io/xorm
➜  go-gin-test git:(server) ✗ go get github.com/go-sql-driver/mysql
go: downloading github.com/go-sql-driver/mysql v1.6.0
go: github.com/go-sql-driver/mysql upgrade => v1.6.0
➜  go-gin-test git:(server) ✗ go get xorm.io/xorm
go: xorm.io/xorm upgrade => v1.0.7
➜  go-gin-test git:(server) ✗ 
复制代码
dbdb.go
db.go
package db

import (
	"fmt"
	"runtime/debug"

	//这个是必须的驱动!注意这里不会自动引入
	_ "github.com/go-sql-driver/mysql"

	"xorm.io/xorm"
)

var MysqlDB *xorm.Engine

//这里可以改造为从yaml文件中获取配置。
func InitDB() error {
	//防止程序崩溃
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(fmt.Errorf("InitMysql:%+v", err))
			fmt.Println(fmt.Errorf("%s", string(debug.Stack())))
		}
	}()
	//记得修改此处的用户、密码等
	url := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True",
		"root",
		"jimbir8520",
		"106.52.197.141",
		"7001",
		"ycc")
	//新建连接
	db, err := xorm.NewEngine("mysql", url)
	if err != nil {
		fmt.Println(fmt.Sprintf("db init failed : %s", err.Error()))
		return err
	}
	MysqlDB = db
	fmt.Println("init db ok!")
	return nil
}


复制代码

到这里,我们的web工程就拥有了使用数据库的能力,接下来,我们尝试写一个业务接口

写一个业务接口

696502307超级英雄吉姆t_user
idnick_nameuser_namepwdcreate_timedeleted
modeluser.go
package model

import "time"

type TUser struct {
	Id         int64     `xorm:"pk autoincr BIGINT(20)"`
        //昵称
	NickName   string    `xorm:"VARCHAR(255)"`
        //用户名
	UserName   string    `xorm:"VARCHAR(255)"`
        //密码
	Pwd        string    `xorm:"VARCHAR(255)"`
        //是否已删除.0为正常,1为已删除
	Deleted    int       `xorm:"default 0 TINYINT(1)"`
        CreateTime time.Time `xorm:"not null comment('创建时间') DATETIME"`

}

//用来检查参数是否正确
func (c *TUser) Check() error {

//用来检查参数是否正确
func (c *TUser) Check() error {
	if c.UserName == "" {
		return errors.New("用户名不能为空")
	}
	if len(c.UserName) < 5 {
		return errors.New("用户名必须大于4位")
	}
	user := &TUser{
		UserName: c.UserName,
	}
	get, err := db.MysqlDB.Get(user)
	if err != nil {
		fmt.Println(fmt.Errorf("Check Get err : %v", err))
		return errors.New("服务器繁忙请稍后重试")
	}
	if get {
		return errors.New("用户名已存在")
	}

	if c.Pwd == "" {
		return errors.New("密码不能为空")
	}

	compile, err := regexp.Compile(`^(.{6,16}[^0-9]*[^A-Z]*[^a-z]*[a-zA-Z0-9]*)$`)
	if err != nil {
		fmt.Println(fmt.Errorf("Check regexp err : %v", err))
		return errors.New("服务器繁忙请稍后重试")
	}
	b := compile.MatchString(c.Pwd)
	if !b {
		return errors.New("密码不符合规则!密码应为6-16位(可以包含字母、数字、下划线)")
	}

	return nil
}



复制代码
apiapi.go
api.go
package api

import (
	"fmt"
	"net/http"
	"time"

	"example.com/m/v2/db"
	"example.com/m/v2/model"
	"github.com/gin-gonic/gin"
)

func Register(c *gin.Context) {
	//此处我试用的json格式传输数据,通常情况下这里应该是form形式的提交,这里是我个人的喜好,比较方便,可灵活变换
	u := &model.TUser{}
	err := c.BindJSON(u)
	if err != nil {
		fmt.Println(fmt.Errorf("Register BindJSON err : %v", err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": "数据格式不正确",
		})
		return
	}
	err = u.Check()
	if err != nil {
		fmt.Println(fmt.Errorf("Register Check err : %v", err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": err.Error(),
		})
		return
	}
	u.CreateTime = time.Now()
	_, err = db.MysqlDB.Insert(u)
	if err != nil {
		fmt.Println(fmt.Errorf("Register Insert err : %v", err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"msg": "ok",
	})
}


复制代码
router.go
package routerex

import (
	"example.com/m/v2/api"
	"example.com/m/v2/routerex/mid"
	"github.com/gin-gonic/gin"
)

//用作注册请求地址
func RegRouter(g *gin.Engine) {
	g1 := g.Group("/api")
	//中间件
	g1.Use(mid.MidCors)
	g1.POST("/register", api.Register)
}

复制代码
 go build -o hello./hello
➜  go-gin-test git:(server) ✗ go build -o hello
➜  go-gin-test git:(server) ✗ ./hello          
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /api/register             --> example.com/m/v2/api.Register (4 handlers)
init db ok!
[GIN-debug] Listening and serving HTTP on :8080

复制代码
postman

可以看到我们请求成功了~

总结

超级英雄吉姆gin超级英雄吉姆