所有文章 > API开发 > 结合gin+gorm+go-Redis写一个基础 API

结合gin+gorm+go-Redis写一个基础 API

上篇

初始化

新建目录,初始化项目,运行:

code

go mod init sai0556/gin-frame

// 使用本地module
go mod edit -require=local.com/sai0556/gin-frame@v1.0.0
go mod edit -replace=local.com/sai0556/gin-frame@v1.0.0=$PWD

编码

配置部分

新建config目录,初始化并监听文件:

go

package config

import (
"fmt"

"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)

type Config struct {
Name string
}

// 初始化配置
func Init(cfg string) error {
c := Config{
Name: cfg,
}

if err := c.initConfig(); err != nil {
return err
}

c.watchConfig()

return nil
}

func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name)
} else {
// 默认配置文件是conf/config.yaml
viper.AddConfigPath("conf")
viper.SetConfigName("config")
}

viper.SetConfigType("yaml")
// viper解析配置文件
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
fmt.Println(viper.GetString("name"))

return nil
}

func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
}

conf/config.yaml,语法可自行研究下,比较简单。
YAML入门

code

name: gin-frame
db:
name: blog
host: 127.0.0.1:3306
username: root
password: 111111
charset: utf8mb4

数据库gorm

连接数据库,构建连接池:

go

package model

import (
"fmt"
"sync"
"errors"

orm "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/spf13/viper"
)

type MySqlPool struct {}

var instance *MySqlPool
var once sync.Once

var db *orm.DB
var err error

// 单例模式
func GetInstance() *MySqlPool {
once.Do(func() {
instance = &MySqlPool{}
})

return instance
}

func (pool *MySqlPool) InitPool() (isSuc bool) {
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s", viper.GetString("db.username"), viper.GetString("db.password"), viper.GetString("db.host"), viper.GetString("db.name"), viper.GetString("db.charset"))
db, err = orm.Open("mysql", dsn)
if err != nil {
panic(errors.New("mysql连接失败"))
return false
}

// 连接数配置也可以写入配置,在此读取
db.DB().SetMaxIdleConns(50)
db.DB().SetMaxOpenConns(50)
// db.LogMode(true)
return true
}

main.go

我们完善一下main.go,初始化配置,并构建连接池:

go

package main
// import 这里我习惯把官方库,开源库,本地module依次列出
import (
"log"
"os"
"errors"

"github.com/spf13/pflag"

"local.com/sai0556/gin-frame/config"
"local.com/sai0556/gin-frame/model"
)

var (
conf = pflag.StringP("config", "c", "", "config filepath")
)

func main() {
pflag.Parse()

// 初始化配置
if err := config.Init(*conf); err != nil {
panic(err)
}

// 连接mysql数据库
isSuc := model.GetInstance().InitPool()
if !isSuc {
log.Println("init database pool failure...")
panic(errors.New("init database pool failure"))
}
}


写完不妨运行一下,看看效果吧!

code

go run main.go -c=./conf/config.yaml

中篇


在上篇里,我介绍了读取配置,并尝试连接了数据库,那么这一篇呢,我们主要利用gin框架来写写简单的接口。


路由

为了便于管理,还是将路由文件单独出来,新建routes:

go

package router

import (
"net/http"

"github.com/gin-gonic/gin"

"local.com/sai0556/gin-frame/controller"
)

func Load(g *gin.Engine) *gin.Engine {
g.Use(gin.Recovery())
// 404
g.NoRoute(func (c *gin.Context) {
c.String(http.StatusNotFound, "404 not found");
})

g.GET("/", controller.Index)

return g
}

控制器

上面的代码中我们看到了controller,我们建一个目录controller:

先建base.go文件,用于写一些基础的方法,如SendResponse返回json。

go

package controller

import (
"net/http"

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

type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

func SendResponse(c *gin.Context, code int, message string, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
Data: data,
})
}

再来写个index.go,处理逻辑。

go

package controller

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


func Index(c *gin.Context) {
SendResponse(c, 0, "success", nil)
}

启动gin

go

// main.go
// 在连接数据库后加入以下代码

gin.SetMode("debug")
g := gin.New()
g = router.Load(g)

g.Run(":8080")

不妨启动看看效果。

go run main.go -c=./conf/config.yaml

当然,这里的服务启动、停止可以写得再优雅一些。

下篇

前两篇我们已经完成了gin+gorm部分,今天我们来补充go-Redis,并进行测试。

整合go-Redis

我们把Redis相关也放在model下面,使用的是常见的go-redis:

go

// redis.go
package model

import (
"fmt"

"github.com/spf13/viper"
"github.com/go-redis/redis"
)

var RedisClient *redis.Client

func RedisInit() {
RedisClient = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%s", viper.GetString("redis.host"), viper.GetString("redis.port")),
Password: viper.GetString("redis.auth"),
DB: 0,
})

_, err := RedisClient.Ping().Result()
if err != nil {
panic("redis ping error")
}
}


然后在连接Mysql的前面加入初始化redis连接的操作即可。

go

// redis 初始化
model.RedisInit()

你可以做一些简单操作,或者在redis.go做一些常用方法的封装,比较简单,就不赘述了,更多go-redis操作可见:

  • go-redis
  • github太慢就看这个

测试

新建测试目录test,建立三个文件:

// index.go
package test

import (
"net/http"
"io/ioutil"
)

func Sum(a int, b int) int {
return a+b
}

func HttpIndex() []byte {
resp, err := http.Get("http://127.0.0.1:8080/")
if err != nil && resp.StatusCode != 200 {
panic(err)
}
//关闭连接
defer resp.Body.Close()
//读取报文中所有内容
body, err := ioutil.ReadAll(resp.Body)

if err != nil {
panic(err)
}
//输出内容
return body
// index_test.go
package test

import (
"testing"
"encoding/json"

"local.com/sai0556/gin-frame/controller"
)

func TestSum(t *testing.T) {
ret := Sum(2, 7)
if ret != 9 {
t.Error("Expected 9 ~wow~")
}
}

func TestHttpIndex(t *testing.T) {
data := HttpIndex()

target := controller.Response{}
// json转换
if err := json.Unmarshal(data, &target); err != nil {
t.Error(target)
}

ret := controller.Response{0, "success", nil}

if target != ret {
t.Error("json error")
}
}
// index_bench_test.go
package test

import (
"testing"
)

func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
Sum(2, 7)
}
}

func BenchmarkHttpIndex(b *testing.B) {

for i := 0; i < b.N; i++ {
HttpIndex()
}
}

私以为go的测试相比其他语言还是比较简洁的,这里需要注意几点:

  1. 测试文件以_test结尾
  2. 基础测试方法名要以TEST开头

运行起来,看一下:

对图中做一些说明:

code

// -run="none"不执行基础单元测试,bench指定基准测试方法
go test -v -run="none" -bench="Bench*"

// 最后一个BenchmarkHttpIndex-4后面测试结果表示
一共执行了11010次,每次执行耗时107392ns(~0.107ms)

test标准库


结后语

文章很基础,主要是介绍了结合了gin+gorm+go-redis,并写了简单的测试,是相对基础的文章,但足以应付一些api接口了。希望对你有帮助,有问题可留言或私信。

点击查看项目DEMO

文章转自微信公众号@SaiWeng

#你可能也喜欢这些API文章!