GoFrame带你从0-1快速入门

GoFrame:带你从0-1快速入门

由于项目的需要,自己开始了解GoFrame这个框架,网上没有视频学习,所以开始看官网学习,在学习的时候,感觉GoFrame真的是一个不错的框架,整理自己的笔记。大部分和github上的一样,但是本文进行整合,方便大家的学习。

一.GoFrame介绍:

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种跨平台(Mac OS、Windows、Linux 等)静态强类型、编译型语言。由Ken Thompson(肯·汤普森)联合创立,Unix操作系统的发明人之一(排在第一号)。

  • docker,golang头号优秀项目,通过虚拟化技术实现的操作系统与应用的隔离,也称为容器;
  • kubernetes,是来自 Google 云平台的开源容器集群管理系统。简称k8s,k8s和docker是当前容器化技术的重要基础设施;

golang基础教程-快速入门go语言

github:https://github.com/goflyfox/gostudy

gitee:https://gitee.com/flyfox/gostudy

1.1GF基本介绍

GF(Go Frame)是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。

1.2GF特点

  • 模块化、松耦合设计;
  • 模块丰富,开箱即用;
  • 简便易用,易于维护;
  • 社区活跃,大牛谦逊低调脾气好;
  • 高代码质量、高单元测试覆盖率;
  • 详尽的开发文档及示例;
  • 完善的本地中文化支持;
  • 更适合企业及团队使用;

1.3GF地址

目录结构及基本介绍:

GF
├── container -- 基础类型:数组,通道,列表,map,队列,环,set,树,类型处理和转换
│   ├── garray
│   ├── gchan
│   ├── glist
│   ├── gmap
│   ├── gpool
│   ├── gqueue
│   ├── gring
│   ├── gset
│   ├── gtree
│   ├── gtype
│   └── gvar
├── crypto  -- 加密和解密:常用的md5,aes,3des
│   ├── gaes
│   ├── gcrc32
│   ├── gdes
│   ├── gmd5
│   └── gsha1
├── database  -- 数据库:关系型数据库(mysql,postgre,oracle)和redis
│   ├── gdb
│   └── gredis
├── debug    --  调试
│   └── gdebug
├── DONATOR.MD
├── encoding  --编解码:常用的base64和json
│   ├── gbase64
│   ├── gbinary
│   ├── gcharset
│   ├── gcompress
│   ├── ghash
│   ├── ghtml
│   ├── gini
│   ├── gjson
│   ├── gparser
│   ├── gtoml
│   ├── gurl
│   ├── gxml
│   └── gyaml
├── errors  -- 错误处理
│   └── gerror
├── frame   -- 核心框架:web,mvc
│   ├── g
│   ├── gins
│   └── gmvc
├── go.mod
├── i18n   -- 国际化
│   └── gi18n
├── internal  系统:空处理,锁,结构体
│   ├── cmdenv
│   ├── empty
│   ├── fileinfo
│   ├── intlog
│   ├── mutex
│   ├── rwmutex
│   ├── structs
│   └── utils
├── LICENSE
├── net   -- 网络:http,tpc,udp
│   ├── ghttp
│   ├── gipv4
│   ├── gipv6
│   ├── gsmtp
│   ├── gtcp
│   └── gudp
├── os    -- 系统:定时任务,命令行交互,日志,文件处理,缓存,session,时间
│   ├── gbuild
│   ├── gcache
│   ├── gcfg
│   ├── gcmd
│   ├── gcron
│   ├── genv
│   ├── gfcache
│   ├── gfile
│   ├── gfpool
│   ├── gfsnotify
│   ├── glog
│   ├── gmlock
│   ├── gmutex
│   ├── gproc
│   ├── gres
│   ├── grpool
│   ├── gsession
│   ├── gspath
│   ├── gtime
│   ├── gtimer
│   └── gview
├── README.MD
├── README_ZH.MD
├── RELEASE.1.MD
├── RELEASE.2.MD
├── test  -- 单元测试
│   └── gtest
├── text  -- 文本处理:正则,字符串处理
│   ├── gregex
│   └── gstr
├── TODO.MD
├── util  -- 常用工具:类型转换,随机数,uuid,校验
│   ├── gconv
│   ├── gmode
│   ├── gpage
│   ├── grand
│   ├── gutil
│   ├── guuid
│   └── gvalid
└── version.go

1.4GF框架

img

二.GoFrame基础环境搭建

2.1环境搭建

  • 之前基础教程有golang环境安装详细介绍,这里我只是快速过一下;
1)安装golang

这里仅以windows为例:(苹果用户直接在 ~/.bash_profile 或者 ~/.zshrc (看自己电脑情况而定)中配置即可)

  1. 去中文社区下载安装golang:https://studygolang.com/dl;
  2. 下载go.{version}.windows-amd64.msi或者go.{version}.windows-amd64.zip包,此次使用go.{version}.windows-amd64.zip包
  3. 解压压缩文件(这里使用的是D:\Project,后面都基于这个目录)
  4. 配置环境变量GOPATH和GOROOT
# 打开cmd设置
set GOPATH=D:\Project\GOPATH
set GOROOT=D:\Project\GO
set PATH=%PATH%;%GOROOT%\bin

当然应该将这些环境变量配置到系统环境变量中

  1. 此时打开cmd窗口,运行go version即可展示安装golang版本
# go version
go version go1.14 windows/amd64
2)安装goland
  1. 官网下载goland:https://www.jetbrains.com/go/
  2. 安装注册购买或者破解;
  3. 首先打开File->Setting或者Ctrl+Alt+S,设置goroot和gopath,默认会获取环境变量配置
  4. 需要开启go modules功能,然后配置代理;不配置代理会访问国外地址,会很慢;建议使用以下三个地址:(选择一个即可)
  • https://goproxy.io
  • https://goproxy.cn
  • https://mirrors.aliyun.com/goproxy/

大家想详细了解可以看我的另一篇博客:https://blog.csdn.net/weixin_51261234/article/details/123555904?spm=1001.2014.3001.5501,此中有代理的详细设置和介绍

3) 了解go modules

go.mod是Go项目的依赖描述文件:

module hello

go 1.14

require github.com/gogf/gf v1.11.7
  1. module是配置项目名称
  2. go配置的是使用的golang版本
  3. require配置引用第三方依赖包路径和版本,latest表示最新版本;

配置完编译成功后,生成go.sum依赖分析结果,里面会有当前所有的依赖详细信息;

2.2GF运行普通项目

通过go.mod引用goframe,构建下载,打印版本号;项目文件如下:

go.mod
module hello

go 1.14

require github.com/gogf/gf v1.11.7
hello.go
package main

import (
	"fmt"
	"github.com/gogf/gf"
)

func main() {
	fmt.Println("hello world!")
    // 打印GF版本
	fmt.Println(gf.VERSION)
}

2.3GF搭建web项目

让我们来运行第一个web程序

go.mod
module hello

go 1.14

require github.com/gogf/gf v1.11.7
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
)

func main() {
	s := g.Server()
	s.BindHandler("/", func(r *ghttp.Request){
		r.Response.Writeln("Welcome GoFrame!")
	})
	s.BindHandler("/hello", func(r *ghttp.Request){
		r.Response.Writeln("Hello World!")
	})

	s.SetPort(80)
	s.Run()
}

运行然后打开浏览器,访问:http://127.0.0.1/和http://127.0.0.1/hello查看效果

三. GoFrame的Web服务介绍

GF框架提供了非常强大的WebServer,由ghttp模块实现。实现了丰富完善的相关组件,例如:Router、Cookie、Session、路由注册、配置管理、模板引擎、缓存控制等等,支持热重启、热更新、多域名、多端口、多实例、HTTPS、Rewrite等等特性。

3.1web基本介绍

  • 我们的电脑浏览器(Browser)就是客户端(Client),大型的服务器就是服务端(Server);浏览器发送HTTP请求,即客户端通过网络将需求发给服务端,然后服务端也是通过网络将数据发给客户端;

image-20200319225300542

3.2GF搭建web项目

  • 这里主要介绍基本项目启动和配置参数

    web:.go.mod   -- go module
    │  go.sum
    │  main.go  -- 启动文件
    │
    ├─config
    │      config.toml --配置文件
    │
    ├─gflogs
    │      2020-03-19.log -- gf系统日志
    │      access-20200319.log -- 访问日志
    │      error-20200319.log  -- 异常日志
    │
    ├─logs
    │      2020-03-19.log -- 业务日志
    │
    └─public
            hello.html -- 静态文件
            index.html -- 静态入口文件
    
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/glog"
)

func main() {
	s := g.Server()
	// 测试日志
	s.BindHandler("/welcome", func(r *ghttp.Request) {
		glog.Info("你来了!")
		glog.Error("你异常啦!")
		r.Response.Write("哈喽世界!")
	})
	// 异常处理
	s.BindHandler("/panic", func(r *ghttp.Request) {
		glog.Panic("123")
	})
	// post请求
	s.BindHandler("POST:/hello", func(r *ghttp.Request) {
		r.Response.Writeln("Hello World!")
	})
	s.Run()
}
config.toml

GF框架的核心组件均实现了便捷的文件配置管理方式,包括Server、日志组件、数据库ORM、模板引擎等等,非常强大便捷。

[server]
    # 端口号
    Address          = ":8199"
    # 静态目录
    ServerRoot       = "public"
    # 入口文件
    IndexFiles       = ["index.html", "main.html"]
    # 系统访问日志
    AccessLogEnabled = true
    # 系统异常日志panic
    ErrorLogEnabled  = true
    # 系统日志目录,启动,访问,异常
    LogPath          = "gflogs"

[logger]
    # 标准日志目录
    path   = "logs"
    # 日志级别
    level  = "all"

四.GoFrame路由注册

4.1路由规则

gf框架自建了非常强大的路由功能,提供了比任何同类框架更加出色的路由特性,支持流行的命名匹配规则、模糊匹配规则及字段匹配规则,并提供了优秀的优先级管理机制。

该方法是路由注册的最基础方法,其中的pattern为路由注册规则字符串,在其他路由注册方法中也会使用到,参数格式如下:

[HTTPMethod:]路由规则[@域名]

其中HTTPMethod(支持的Method:GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE)和@域名为非必需参数,一般来说直接给定路由规则参数即可,BindHandler会自动绑定所有的请求方式,如果给定HTTPMethod,那么路由规则仅会在该请求方式下有效。@域名可以指定生效的域名名称,那么该路由规则仅会在该域名下生效。

BindHandler是最原生的路由注册方法,在大部分场景中,我们通常使用 分组路由 方式来管理路由

示例:

	// hello方法,post调用
	s.BindHandler("POST:/hello", func(r *ghttp.Request) {
		r.Response.Writeln("url" + r.Router.Uri)
	})

4.2回调函数注册

回调函数注册方式是最简单且最灵活的的路由注册方式,注册的服务可以是一个实例化对象的方法地址,也可以是一个包方法地址。服务需要的数据可以通过模块内部变量形式或者对象内部变量形式进行管理,开发者可根据实际情况进行灵活控制。

我们可以直接通过BindHandler方法完成回调函数的注册,在框架的开发手册中很多地方都使用了回调函数注册的方式来做演示,因为这种注册方式比较简单。

示例:

	// 方法注册
	s.BindHandler("/total", Total)

4.3执行对象注册

执行对象注册是在注册时便给定一个实例化的对象,以后每一个请求都交给该对象(同一对象)处理,该对象常驻内存不释放。服务端进程在启动时便需要初始化这些执行对象,并且这些对象需要自行负责对自身数据的并发安全维护(往往对象的成员变量应当是并发安全的,每个请求执行完毕后该对象不会销毁,其成员变量也不会释放)。

	// 对象注册
	c := new(Controller)
	s.BindObject("POST:/object", c)

4.4分组注册

GF框架支持分组路由的注册方式,可以给分组路由指定一个prefix前缀(也可以直接给定/前缀,表示注册在根路由下),在该分组下的所有路由注册都将注册在该路由前缀下。分组路由注册方式也是推荐的路由注册方式。

示例:

  // 分组注册及中间件
	group := s.Group("/api")
	group.ALL("/all", func(r *ghttp.Request) {
		r.Response.Writeln("all")
	})

4.5中间件设计

GF提供了优雅的中间件请求控制方式,该方式也是主流的WebServer提供的请求流程控制方式,基于中间件设计可以为WebServer提供更灵活强大的插件机制。经典的中间件洋葱模型:

经典的中间件洋葱模型

事例:

  // 分组注册及中间件
	group := s.Group("/api")
	group.Middleware(MiddlewareTest)
	group.ALL("/all", func(r *ghttp.Request) {
		r.Response.Writeln("all")
	})

4.6请求和响应对象

4.6.1请求Request

请求输入依靠 ghttp.Request 对象实现,ghttp.Request继承了底层的http.Request对象。ghttp.Request包含一个与当前请求对应的返回输出对象Response,用于数据的返回处理。

可以看到Request对象的参数获取方法非常丰富,可以分为以下几类:

  1. Get*: 常用方法,简化参数获取,GetRequest*的别名。
  2. GetQuery*: 获取GET方式传递过来的参数,包括Query StringBody参数解析。
  3. GetForm*: 获取表单方式传递过来的参数,表单方式提交的参数Content-Type往往为application/x-www-form-urlencoded, application/form-data, multipart/form-data, multipart/mixed等等。
  4. GetRequest*: 获取客户端提交的参数,不区分提交方式。
  5. Get*Struct: 将指定类型的请求参数绑定到指定的struct对象上,注意给定的参数为对象指针。绝大部分场景中往往使用Parse方法将请求数据转换为请求对象,具体详见后续章节。
  6. GetBody/GetBodyString: 获取客户端提交的原始数据,该数据是客户端写入到body中的原始数据,与HTTP Method无关,例如客户端提交JSON/XML数据格式时可以通过该方法获取原始的提交数据。
  7. GetJson: 自动将原始请求信息解析为gjson.Json对象指针返回。
  8. Exit*: 用于请求流程退出控制;
4.6.2 响应Response

ghttp.Response对象实现了标准库的http.ResponseWriter接口。数据输出使用Write*相关方法实现,并且数据输出采用了Buffer机制,因此数据的处理效率比较高。任何时候可以通过OutputBuffer方法输出缓冲区数据到客户端,并清空缓冲区数据。

简要说明:

  1. Write*方法用于数据的输出,可为任意的数据格式,内部通过断言对参数做自动分析。
  2. Write*Exit方法用于数据输出后退出当前服务方法,可用于替代return返回方法。
  3. WriteJson*/WriteXml方法用于特定数据格式的输出,这是为开发者提供的简便方法。
  4. WriteTpl*方法用于模板输出,解析并输出模板文件,也可以直接解析并输出给定的模板内容。
  5. ParseTpl*方法用于模板解析,解析模板文件或者模板内容,返回解析后的内容。

4.7教程示例

package main

import (
	"github.com/gogf/gf/container/gtype"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
)

func main() {
	s := g.Server()
	// 常规注册
	// hello方法,post调用
	s.BindHandler("POST:/hello", func(r *ghttp.Request) {
		r.Response.Writeln("url" + r.Router.Uri)
	})
	// 所有方法,url包含name参数
	s.BindHandler("/:name", func(r *ghttp.Request) {
		// 获取URL name参数
		r.Response.Writeln("name:" + r.GetString("name"))
		r.Response.Writeln("url" + r.Router.Uri)
	})
	// 所有方法,url包含name参数
	s.BindHandler("/:name/update", func(r *ghttp.Request) {
		r.Response.Writeln("name:" + r.GetString("name"))
		r.Response.Writeln("url" + r.Router.Uri)
	})
	// 所有方法,url包含name和action参数
	s.BindHandler("/:name/:action", func(r *ghttp.Request) {
		r.Response.Writeln("name:" + r.GetString("name"))
		r.Response.Writeln("action:" + r.GetString("action"))
		r.Response.Writeln("url" + r.Router.Uri)
	})
	// 所有方法,url包含field属性
	s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request) {
		// 获取URL field属性
		r.Response.Writeln("field:" + r.GetString("field"))
		r.Response.Writeln("url" + r.Router.Uri)
	})

	// 方法注册
	s.BindHandler("/total", Total)

	// 对象注册
	c := new(Controller)
	s.BindObject("POST:/object", c)


	// 分组注册及中间件
	group := s.Group("/api")
	group.Middleware(MiddlewareTest)
	group.ALL("/all", func(r *ghttp.Request) {
		r.Response.Writeln("all")
	})
	group.GET("/get", func(r *ghttp.Request) {
		r.Response.Writeln("get")
	})
	group.POST("/post", func(r *ghttp.Request) {
		r.Response.Writeln("post")
	})

	// request and response
	s.BindHandler("POST:/test", func(r *ghttp.Request) {
		r.Response.WriteJson(g.Map{
			"name":r.GetString("name"),
			"age":r.GetInt("age"),
			"sex":r.Header.Get("sex"),
		})
	})

	s.SetPort(8199)
	s.Run()
}

var (
	total = gtype.NewInt()
)

func Total(r *ghttp.Request) {
	r.Response.Write("total:", total.Add(1))
}

// 对象注册
type Controller struct{}

func (c *Controller) Index(r *ghttp.Request) {
	r.Response.Write("index")
}

func (c *Controller) Show(r *ghttp.Request) {
	r.Response.Write("show")
}

// 中间件
func MiddlewareTest(r *ghttp.Request) {
	// 前置逻辑
	r.Response.Writeln("###start")
	r.Middleware.Next()
	// 后置逻辑
	r.Response.Writeln("###end")
}

访问结果:

### 常规注册
POST http://localhost:8199/hello

###
GET http://localhost:8199/abc

###
GET http://localhost:8199/a/add

###
GET http://localhost:8199/a/update

###
GET http://localhost:8199/user/list/11.html

### 方法注册
GET http://localhost:8199/total

### 对象注册,默认访问index
POST http://localhost:8199/object/

### 对象注册,直接访问Index
POST http://localhost:8199/object/index

### 对象注册,访问show方法
POST http://localhost:8199/object/show

### 分组,默认访问index
PUT http://localhost:8199/api/all

### 对象注册,直接访问Index
GET http://localhost:8199/api/get

### 对象注册,访问show方法
POST http://localhost:8199/api/post

### request and response
POST http://localhost:8199/test
sex:man

name=liubang&age=18

###

五. GoFrame的HTTP客户端

5.1TTP协议介绍

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。

请求:

image-20200316235850132

响应:

image-20200316235911263

5.2GF的HTTP客户端

  • 这个先启动一个gf的http server,然后我们通过go test 来测试ghttp client;
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
)

func main() {
	s := g.Server()
	group := s.Group("/api")
	// 默认路径
	group.ALL("/", func(r *ghttp.Request) {
		r.Response.Writeln("Welcome GoFrame!")
	})
	// GET带参数
	group.GET("/hello", func(r *ghttp.Request) {
		r.Response.Writeln("Hello World!")
		r.Response.Writeln("name:", r.GetString("name"))
	})
	// POST KV
	group.POST("/test", func(r *ghttp.Request) {
		r.Response.Writeln("func:test")
		r.Response.Writeln("name:", r.GetString("name"))
		r.Response.Writeln("age:", r.GetInt("age"))
	})
	// POST JSON
	group.POST("/test2", func(r *ghttp.Request) {
		r.Response.Writeln("func:test2")
		r.Response.Writeln("passport:", r.GetString("passport"))
		r.Response.Writeln("password:", r.GetString("password"))
	})
	// POST Header
	group.POST("/test3", func(r *ghttp.Request) {
		r.Response.Writeln("func:test3")
		r.Response.Writeln("Cookie:", r.Header.Get("Cookie"))
	})
	// POST Header
	group.POST("/test4", func(r *ghttp.Request) {
		r.Response.Writeln("func:test4")
		h := r.Header
		r.Response.Writeln("accept-encoding:", h.Get("accept-encoding"))
		r.Response.Writeln("accept-language:", h.Get("accept-language"))
		r.Response.Writeln("referer:", h.Get("referer"))
		r.Response.Writeln("cookie:", h.Get("cookie"))
		r.Response.Writeln(r.Cookie.Map())
	})

	s.SetPort(80)
	s.Run()
}
client_test.go

单元测试源码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,例如:

func TestXXX( t *testing.T )

  • 测试用例文件不会参与正常源码编译,不会被包含到可执行文件中。
  • 测试用例文件使用go test指令来执行,没有也不需要 main() 作为函数入口。所有在以_test结尾的源码内以Test开头的函数会自动被执行。
  • 测试用例可以不传入 *testing.T 参数。
package test

import (
	"fmt"
	"github.com/gogf/gf/net/ghttp"
	"testing"
)

var path = "http://127.0.0.1/api"

// GET请求
func TestGet(t *testing.T) {
	if response, err := ghttp.Get(path); err != nil {
		panic(err)
	} else {
		defer response.Close()
		t.Log(response.ReadAllString())
	}
	if response, err := ghttp.Post(path); err != nil {
		panic(err)
	} else {
		defer response.Close()
		t.Log(response.ReadAllString())
	}
}

// GET请求带参数
func TestHello(t *testing.T) {
	if response, err := ghttp.Get(path + "/hello?name=whoami"); err != nil {
		panic(err)
	} else {
		defer response.Close()
		t.Log(response.ReadAllString())
	}
}

// POST请求
func TestPost(t *testing.T) {
	if response, err := ghttp.Post(path+"/test", "name=john&age=18"); err != nil {
		panic(err)
	} else {
		defer response.Close()
		t.Log(response.ReadAllString())
	}
}

// POST JSON
func TestPostJson(t *testing.T) {
	if response, err := ghttp.Post(path+"/test2",
		`{"passport":"john","password":"123456"}`); err != nil {
		panic(err)
	} else {
		defer response.Close()
		t.Log(response.ReadAllString())
	}
}

// POST Header头
func TestPostHeader(t *testing.T) {
	c := ghttp.NewClient()
	c.SetHeader("Cookie", "name=john; score=100")
	if r, e := c.Post(path + "/test3"); e != nil {
		panic(e)
	} else {
		fmt.Println(r.ReadAllString())
	}
}

// POST Header头
func TestPostHeader2(t *testing.T) {
	c := ghttp.NewClient()
	c.SetHeaderRaw(`
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8
referer: https://idonottell.you
cookie: name=john; score=100
user-agent: my test http client
 	`)
	if r, e := c.Post(path + "/test4"); e != nil {
		panic(e)
	} else {
		fmt.Println(r.ReadAllString())
	}
}

六.GoFrame配置文件

6.1配置文件介绍

GF的配置管理由gcfg模块实现,gcfg模块是并发安全的,仅提供配置文件读取功能,不提供数据写入/修改功能,支持的数据文件格式包括: JSONXMLYAML/YMLTOMLINI,项目中开发者可以灵活地选择自己熟悉的配置文件格式来进行配置管理。

默认读取执行文件所在目录及其下的config目录,默认读取的配置文件为config.toml;toml类型文件也是默认的、推荐的配置文件格式,如果想要自定义文件格式,可以通过SetFileName方法修改默认读取的配置文件名称(如:config.json, cfg.yaml, cfg.xml, cfg.ini等等)。

注:TOML大小写敏感,必须是UTF-8编码;

6.2自动检测更新

配置管理器使用了缓存机制,当配置文件第一次被读取后会被缓存到内存中,下一次读取时将会直接从缓存中获取,以提高性能。同时,配置管理器提供了对配置文件的自动检测更新机制,当配置文件在外部被修改后,配置管理器能够即时地刷新配置文件的缓存内容。

配置管理器的自动检测更新机制是gf框架特有的一大特色。

6.3示例

项目目录
D:.
│  config_test.go -- 测试文件
│  go.mod
│  go.sum
│  main.go -- web自动更新配置演示
│
├─config
│      config.toml -- 标准配置文件
│
└─configTest -- 定制目录和配置文件
        config1.toml  
        config2.toml
config.toml
# 模板引擎目录
viewpath = "/home/www/templates/"
# MySQL数据库配置
[database]
    [[database.default]]
        host     = "127.0.0.1"
        port     = "3306"
        user     = "root"
        pass     = "123456"
        name     = "test1"
        type     = "mysql"
        role     = "master"
        charset  = "utf8"
        priority = "1"
    [[database.default]]
        host     = "127.0.0.1"
        port     = "3306"
        user     = "root"
        pass     = "123456"
        name     = "test2"
        type     = "mysql"
        role     = "master"
        charset  = "utf8"
        priority = "1"
# Redis数据库配置
[redis]
    disk  = "127.0.0.1:6379,0"
    cache = "127.0.0.1:6379,1"
config1.toml
study = "hello study"
study1 = "hello study1"
config2.toml
config2 = "111"
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
)

func main() {
	s := g.Server()
	// 默认路径
	s.BindHandler("/", func(r *ghttp.Request) {
		r.Response.Writeln("配置",g.Config().GetString("name"))
		r.Response.Writeln("Welcome GoFrame!")
	})

	s.SetPort(80)
	s.Run()

}
config_test.go
package main

import (
	"fmt"
	"github.com/gogf/gf/frame/g"
	"testing"
)

// 基本配置使用
func TestConfig(t *testing.T) {
	// 默认当前路径或者config路径,默认文件config.toml
	// /home/www/templates/
	fmt.Println(g.Config().Get("viewpath"))
	fmt.Println(g.Cfg().Get("viewpath"))
	// 127.0.0.1:6379,1
	c := g.Cfg()
	// 分组方式
	fmt.Println(c.Get("redis.cache"))
	// 数组方式:test2
	fmt.Println(c.Get("database.default.1.name"))
}

// 设置路径
func TestConfig2(t *testing.T) {
	// 设置加载文件,默认name为default
	// 设置路径
	g.Cfg().SetPath("configTest")
	// 设置加载文件
	g.Cfg().SetFileName("config1.toml")

	// 打印测试
	fmt.Println(g.Cfg().Get("viewpath"))
	fmt.Println(g.Cfg().Get("study"))
	fmt.Println(g.Cfg().Get("study1"))
	fmt.Println(g.Cfg().Get("config2"))

	// 新的name就是新的实例
	g.Cfg("name").SetPath("configTest")
	g.Cfg("name").SetFileName("config2.toml")
	fmt.Println(g.Cfg("name").Get("viewpath"))
	fmt.Println(g.Cfg("name").Get("study"))
	fmt.Println(g.Cfg("name").Get("study1"))
	fmt.Println(g.Cfg("name").Get("config2"))
}
go.mod
module gf_config

go 1.14

require github.com/gogf/gf v1.11.7

七. GoFrame日志打印

7.1日志介绍

  • glog是通用的高性能日志管理模块,实现了强大易用的日志管理功能,是gf开发框架的核心模块之一。

  • 重要的几点说明:

  1. glog采用了无锁设计,性能高效;
  2. glog支持文件输出、日志级别、日志分类、调试管理、调用跟踪、链式操作等等丰富特性;
  3. 可以使用glog.New方法创建glog.Logger对象用于自定义日志打印,也可以并推荐使用glog默认提供的包方法来打印日志;
  4. 当使用包方法修改模块配置时,注意任何的glog.Set*设置方法都将会全局生效
  5. 日志内容默认时间格式为 时间 [级别] 内容 换行,其中时间精确到毫秒级别,级别为可选输出,内容为调用端的参数输入,换行为可选输出(部分方法自动为日志内容添加换行符号),日志内容示例:2018-10-10 12:00:01.568 [ERRO] 产生错误
  6. Print*/Debug*/Info*方法输出日志内容到标准输出(stdout),为防止日志的错乱,Notice*/Warning*/Error*/Critical*/Panic*/Fatal*方法也是将日志内容输出到标准输出(stdout);
  7. 其中Panic*方法在输出日志信息后会引发panic错误方法,Fatal*方法在输出日志信息之后会停止进程运行,并返回进程状态码值为1(正常程序退出状态码为0);

7.2单例对象

  • GF v1.10版本开始,日志组件支持单例模式,使用g.Log(单例名称)获取不同的单例日志管理对象。提供单例对象的目的在于针对不同业务场景可以使用不同配置的日志管理对象。

7.3日志级别

  • 日志级别用于管理日志的输出,我们可以通过设定特定的日志级别来关闭/开启特定的日志内容。通过SetLevel方法可以设置日志级别,glog支持以下几种日志级别常量设定:

    LEVEL_ALL  
    LEVEL_DEBU 
    LEVEL_INFO
    LEVEL_NOTI
    LEVEL_WARN
    LEVEL_ERRO
    LEVEL_CRIT
    
  • 我们可以通过位操作组合使用这几种级别,例如其中LEVEL_ALL等价于LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT。例如我们可以通过LEVEL_ALL & ^LEVEL_DEBU & ^LEVEL_INFO & ^LEVEL_NOTI来过滤掉LEVEL_DEBU/LEVEL_INFO/LEVEL_NOTI日志内容。

7.4配置文件

  • 日志组件支持配置文件,当使用g.Log(单例名称)获取Logger单例对象时,将会自动通过默认的配置管理对象获取对应的Logger配置。默认情况下会读取logger.单例名称配置项,当该配置项不存在时,将会读取logger配置项。

    [logger]
        # 日志目录
        path   = "logs"
        # 	all LEVEL_ALL  = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
        #	dev LEVEL_DEV  = LEVEL_ALL
        #	pro LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
        level  = "all"
        # 是否打印到控制台
        stdout = true
        [logger.logger1]
            path   = "logger1"
            level  = "dev"
            stdout = true
        [logger.logger2]
            path   = "logger2"
            level  = "prod"
            stdout = false
    

7.5示例

项目目录
D:.go.mod
│  go.sum
│  main.go
│
└─config
        config.toml
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/glog"
)

func main() {
	// 对应默认配置项 logger,默认default
	g.Log().Debug("[default]Debug")
	g.Log().Info("[default]info")
	g.Log().Warning("[default]Warning")
	g.Log().Error("[default]Error")
	// 对应 logger.logger1 配置项
	g.Log("logger1").Debug("[logger1]Debug")
	g.Log("logger1").Info("[logger1]info")
	g.Log("logger1").Warning("[logger1]Warning")
	g.Log("logger1").Error("[logger1]Error")
	// 对应 logger.logger2 配置项
	g.Log("logger2").Debug("[logger2]Debug")
	g.Log("logger2").Info("[logger2]info")
	g.Log("logger2").Warning("[logger2]Warning")
	g.Log("logger2").Error("[logger2]Error")


	// 日志级别设置,过滤掉Info日志信息
	l := glog.New()
	l.Info("info1")
	l.SetLevel(glog.LEVEL_ALL ^ glog.LEVEL_INFO)
	l.Info("info2")
	// 支持哪些级别
	// LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT

	// 异常
	g.Log().Panic("this is panic!")
	g.Log().Info("............")

}
config.toml
[logger]
    # 日志目录
    path   = "logs"
    # 	all LEVEL_ALL  = LEVEL_DEBU | LEVEL_INFO | LEVEL_NOTI | LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
    #	dev LEVEL_DEV  = LEVEL_ALL
    #	pro LEVEL_PROD = LEVEL_WARN | LEVEL_ERRO | LEVEL_CRIT
    level  = "all"
    # 是否打印到控制台
    stdout = true
    [logger.logger1]
        path   = "logger1"
        level  = "dev"
        stdout = true
    [logger.logger2]
        path   = "logger2"
        level  = "prod"
        stdout = false

八. Goframe数据库操作

8.1基本介绍

gf框架的ORM功能由gdb模块实现,用于常用关系型数据库的ORM操作。其最大的特色在于同时支持mapstruct两种方式操作数据库。gdb默认情况下使用的是map数据类型作为基础的数据表记录载体,开发者无需预先定义数据表记录struct便可直接对数据表记录执行各种操作。这样的设计赋予了开发者更高的灵活度和简便性。

支持的数据库类型:Mysql,SQLite,PostgreSQL,SQLServer,Oracle

8.2配置文件

  • 推荐使用配置文件及单例对象来管理和使用数据库操作。

如果我们使用g对象管理模块中的g.DB("数据库分组名称")方法获取数据库操作对象,数据库对象将会自动读取config.toml配置文件中的相应配置项(通过配置管理模块),并自动初始化该数据库操作的单例对象。

[database]
    [[database.default]]
        link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
    [[database.user]]
        link = "mysql:root:12345678@tcp(127.0.0.1:3306)/user"

注意每一项分组配置均可以是多个节点,支持负载均衡权重策略。如果不使用多节点负载均衡特性,仅使用配置分组特性,也可以简化为如下格式:

[database]
    [database.default]
        link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
    [database.user]
        link = "mysql:root:12345678@tcp(127.0.0.1:3306)/user"

如果仅仅是单数据库节点,不使用配置分组特性,那么也可以简化为如下格式:

[database]
    link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"

不同数据类型对应的linkinfo如下:

数据库类型Linkinfo配置更多参数
mysqlmysql: 账号:密码@tcp(地址:端口)/数据库名称mysql
pgsqlpgsql: user=账号 password=密码 host=地址 port=端口 dbname=数据库名称pq
mssqlmssql: user id=账号;password=密码;server=地址;port=端口;database=数据库名称;encrypt=disablego-mssqldb
sqlitesqlite: 文件绝对路径 (如: /var/lib/db.sqlite3)go-sqlite3
oracleoracle: 账号/密码@地址:端口/数据库名称go-oci8

9.3日志输出配置

gdb支持日志输出,内部使用的是glog.Logger对象实现日志管理,并且可以通过配置文件对日志对象进行配置。默认情况下gdb关闭了DEBUG日志输出,如果需要打开DEBUG信息需要将数据库的debug参数设置为true。以下是为一个配置文件示例:

[database]
    [database.logger]
        Path   = "/var/log/gf-app/sql"
        Level  = "all"
        Stdout = true
    [database.primary]
        link   = "mysql:root:12345678@tcp(127.0.0.1:3306)/user_center"
        debug  = true

其中database.logger即为gdb的日志配置,当该配置不存在时,将会使用日志组件的默认配置

8.4数据结构

为便于数据表记录的操作,ORM定义了5种基本的数据类型:

type Map         map[string]interface{} // 数据记录
type List        []Map                  // 数据记录列表

type Value       *gvar.Var              // 返回数据表记录值
type Record      map[string]Value       // 返回数据表记录键值对
type Result      []Record               // 返回数据表记录列表
  1. MapList用于ORM操作过程中的输入参数类型(与全局类型g.Mapg.List一致,在项目开发中常用g.Mapg.List替换);
  2. Value/Record/Result用于ORM操作的结果数据类型;

8.5数据库操作

Insert/Replace/Save

这三个链式操作方法用于数据的写入,并且支持自动的单条或者批量的数据写入,三者区别如下:

  1. Insert

    使用INSERT INTO语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,返回失败,否则写入一条新数据;

  2. Replace

    使用REPLACE INTO语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,会删除原有的记录,必定会写入一条新记录;

  3. Save

    使用INSERT INTO语句进行数据库写入,如果写入的数据中存在主键或者唯一索引时,更新原有数据,否则写入一条新数据;

在部分数据库类型中,并不支持Replace/Save方法

Update更新方法

Update用于数据的更新,往往需要结合DataWhere方法共同使用。Data方法用于指定需要更新的数据,Where方法用于指定更新的条件范围。同时,Update方法也支持直接给定数据和条件参数。

Delete删除方法

Delete方法用于数据的删除。

Where/And/Or查询条件

这三个方法用于传递查询条件参数,支持的参数为任意的string/map/slice/struct/*struct类型。

Where条件参数推荐使用字符串的参数传递方式(并使用?占位符预处理),因为map/struct类型作为查询参数无法保证顺序性,且在部分情况下(数据库有时会帮助你自动进行查询索引优化),数据库的索引和你传递的查询条件顺序有一定关系。

当使用多个Where方法连接查询条件时,作用同And。 此外,当存在多个查询条件时,gdb会默认将多个条件分别使用()符号进行包含,这种设计可以非常友好地支持查询条件分组。

All/One/Value/Count数据查询

这四个方法是数据查询比较常用的方法:

  1. All 用于查询并返回多条记录的列表/数组。
  2. One 用于查询并返回单条记录。
  3. Value 用于查询并返回一个字段值,往往需要结合Fields方法使用。
  4. Count 用于查询并返回记录数。

此外,也可以看得到这四个方法定义中也支持条件参数的直接输入,参数类型与Where方法一致。但需要注意,其中Value方法的参数中至少应该输入字段参数。

数据库表
CREATE TABLE `user` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `site` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=10000 ;
package test

import (
	"fmt"
	"github.com/gogf/gf/frame/g"
	"testing"
)

// Insert
func TestInsert(t *testing.T) {
	// INSERT INTO `user`(`name`) VALUES('john')
	_, err := g.DB().Table("user").Data(g.Map{"uid": 10000, "name": "john"}).Insert()
	if err != nil {
		panic(err)
	}
}

// Update
func TestUpdate(t *testing.T) {
	// UPDATE `user` SET `name`='john guo' WHERE name='john'
	_, err := g.DB().Table("user").Data("name", "john guo").
		Where("name", "john").Update()
	if err != nil {
		panic(err)
	}
}

// Delete
func TestDelete(t *testing.T) {
	// DELETE FROM `user` WHERE uid=10
	_, err := g.DB().Table("user").Where("uid", 10000).Delete()
	if err != nil {
		panic(err)
	}
}

// Select Where
func TestWhere(t *testing.T) {
	// INSERT INTO `user`(`name`) VALUES('john')
	g.DB().Table("user").Data(g.Map{"uid": 10001, "name": "john"}).Insert()
	g.DB().Table("user").Data(g.Map{"uid": 10002, "name": "john2"}).Insert()
	// 数量
	count, err := g.DB().Table("user").Where("uid", 10001).Count()
	if err != nil {
		panic(err)
	}
	fmt.Println("count:", count)
	// 获取单个值
	v, err := g.DB().Table("user").Where("uid", 10001).Fields("name").Value()
	if err != nil {
		panic(err)
	}
	fmt.Println("name:", v.String())
	// 查询对象
	r, err := g.DB().Table("user").Where("uid", 10002).One()
	if err != nil {
		panic(err)
	}
	fmt.Println("name:", r.Map()["name"])
	// 查询对象
	//l, err := g.DB().Table("user").As("t").Where("t.uid > ?", 10000).All()
	// 也可以简写为 select * from user as t where t.uid > 10000
	l, err := g.DB().Table("user").As("t").All("t.uid > ?", 10000)
	if err != nil {
		panic(err)
	}
	for index, value := range l {
		fmt.Println(index, value["uid"], value["name"])
	}
	g.DB().Table("user").Where("uid", 10001).Delete()
	g.DB().Table("user").Where("uid", 10002).Delete()
}

九. GoFrame Redis操作

  • Redis客户端由gredis模块实现,底层采用了链接池设计。

9.1Redis介绍

Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储。)。性能出色:Redis读取的速度是110000次/s,写的速度是81000次/s。

支持类型

String: 字符串、Hash: 散列、List: 列表、Set: 集合、Sorted Set: 有序集合

PUB/SUB:发布订阅;

在5.0支持了全新数据类型:Streams

使用场景

缓存,登录验证码,消息队列,过滤器,分布式锁,限流等

9.2Redis配置文件

绝大部分情况下推荐使用g.Redis单例方式来操作redis。因此同样推荐使用配置文件来管理Redis配置,在config.toml中的配置示例如下:

# Redis数据库配置
[redis]
    default = "127.0.0.1:6379,0"
    cache   = "127.0.0.1:6379,1,123456?idleTimeout=600"

其中,Redis的配置格式为:host:port[,db,pass?maxIdle=x&maxActive=x&idleTimeout=x&maxConnLifetime=x]

各配置项说明如下:

配置项名称是否必须默认值说明
host-地址
port-端口
db0数据库
pass-授权密码
maxIdle0允许限制的连接数(0表示不闲置)
maxActive0最大连接数量限制(0表示不限制)
idleTimeout60连接最大空闲时间(单位秒,不允许设置为0)
maxConnLifetime60连接最长存活时间(单位秒,不允许设置为0)

其中的defaultcache分别表示配置分组名称,我们在程序中可以通过该名称获取对应配置的redis对象。不传递分组名称时,默认使用redis.default配置分组项)来获取对应配置的redis客户端单例对象。

9.3结果处理

可以看到通过客户端方法Do/Receive获取的数据都是二进制形式[]byte的,需要开发者手动进行数据转换。

当然,gredis模块也提供了DoVar/ReceiveVar方法,用以获取可供方便转换的gvar.Var通用变量结果。

通过gvar.Var的强大转换功能可以转换为任意的数据类型,如基本数据类型Int,String,Strings,或者结构体Struct等等。

9.4示例

目录结构
D:.go.mod
│  go.sum
│  main.go
│
└─config
        config.toml
main.go
package main

import (
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gconv"
)

func main() {
	// redis字符串操作
	g.Redis().Do("SET", "k", "v")
	v, _ := g.Redis().Do("GET", "k")
	g.Log().Info(gconv.String(v))

	// 获取cache链接
	v2, _ := g.Redis("cache").Do("GET", "k")
	g.Log().Info(gconv.String(v2))

	// DoVar转换
	v3, _ := g.Redis().DoVar("GET", "k")
	g.Log().Info(v3.String())

	// setex
	g.Redis().Do("SETEX", "keyEx", 2000, "v4")
	v4, _ := g.Redis().DoVar("GET", "keyEx")
	g.Log().Info(v4.String())

	// list
	g.Redis().Do("RPUSH", "keyList", "v5")
	v5, _ := g.Redis().DoVar("LPOP", "keyList")
	g.Log().Info(v5.String())

	// hash
	g.Redis().Do("HSET", "keyHash", "v1", "v6")
	v6, _ := g.Redis().DoVar("HGET", "keyHash", "v1")
	g.Log().Info(v6.String())

	// set
	g.Redis().Do("SADD", "keySet", "v7")
	v7, _ := g.Redis().DoVar("SPOP", "keySet")
	g.Log().Info(v7.String())

	// sort set
	g.Redis().Do("ZADD", "keySortSet", 1, "v8")
	v8, _ := g.Redis().DoVar("ZREM", "keySortSet", "v8")
	g.Log().Info(v8.Int())

}
config.toml
# Redis数据库配置
[redis]
    default = "127.0.0.1:6379,0"
    cache   = "127.0.0.1:6379,1,123456?idleTimeout=600"

十. Goframe常用工具介绍

10.1gstr字符串处理

  • 字符串处理工具类
示例
	p := fmt.Println
	p("Contains:  ", gstr.Contains("test", "es"))
	p("Count:     ", gstr.Count("test", "t"))
	p("HasPrefix: ", gstr.HasPrefix("test", "te"))
	p("HasSuffix: ", gstr.HasSuffix("test", "st"))
	p("Join:      ", gstr.Join([]string{"a", "b"}, "-"))
	p("Repeat:    ", gstr.Repeat("a", 5))
	p("Replace:   ", gstr.Replace("foo", "o", "0", -1))
	p("Replace:   ", gstr.Replace("foo", "o", "0", 1))
	p("Split:     ", gstr.Split("a-b-c-d-e", "-"))
	p("ToLower:   ", gstr.ToLower("TEST"))
	p("ToUpper:   ", gstr.ToUpper("test"))
	p("Trim:   ", gstr.Trim("  test  "))

10.2g.Map和gmap

g.Map实现type Map = map[string]interface{}

支持并发安全开关选项的map容器,最常用的数据结构。

该模块包含多个数据结构的map容器:HashMapTreeMapListMap

类型数据结构平均复杂度支持排序有序遍历说明
HashMap哈希表O(1)高性能读写操作,内存占用较高,随机遍历
ListMap哈希表+双向链表O(2)支持按照写入顺序遍历,内存占用较高
TreeMap红黑树O(log N)内存占用紧凑,支持键名排序及有序遍历

此外,gmap模块支持多种以哈希表为基础数据结构的常见类型map定义:IntIntMapIntStrMapIntAnyMapStrIntMapStrStrMapStrAnyMap

使用场景

任何map/哈希表/关联数组使用场景,尤其是并发安全场景中。

示例
	// 常规map方法
	p := fmt.Println
	// 初始化
	m2 := g.Map{"a": 1, "b": 2}
	p(m2)
	// 设置
	m2["c"] = 25
	p(m2)
	// 获取
	p(m2["b"])
	// 删除
	delete(m2, "c")
	// 遍历
	for k, v := range m2 {
		p(k, v)
	}

	p("###########################")

	// 创建一个默认的gmap对象,
	// 默认情况下该gmap对象不支持并发安全特性,
	// 初始化时可以给定true参数开启并发安全特性。
	m := gmap.New()
	// 设置键值对
	for i := 0; i < 10; i++ {
		m.Set(i, i)
	}
	// 查询大小
	fmt.Println(m.Size())
	// 批量设置键值对(不同的数据类型对象参数不同)
	m.Sets(map[interface{}]interface{}{
		10: 10,
		11: 11,
	})
	fmt.Println(m.Size())
	// 查询是否存在
	fmt.Println(m.Contains(1))
	// 查询键值
	fmt.Println(m.Get(1))
	// 删除数据项
	m.Remove(9)
	fmt.Println(m.Size())
	// 批量删除
	m.Removes([]interface{}{10, 11})
	fmt.Println(m.Size())
	// 当前键名列表(随机排序)
	fmt.Println(m.Keys())
	// 当前键值列表(随机排序)
	fmt.Println(m.Values())
	// 查询键名,当键值不存在时,写入给定的默认值
	fmt.Println(m.GetOrSet(100, 100))
	// 删除键值对,并返回对应的键值
	fmt.Println(m.Remove(100))
	// 遍历map
	m.Iterator(func(k interface{}, v interface{}) bool {
		fmt.Printf("%v:%v ", k, v)
		return true
	})
	// 清空map
	m.Clear()
	// 判断map是否为空
	fmt.Println(m.IsEmpty())

10.3gjson

  • gjson模块实现了强大的JSON编码/解析,支持数据层级检索、动态创建修改Json对象,并支持常见数据格式的解析和转换等特点。
示例
    // 创建json
	jsonContent := `{"name":"john", "score":"100"}`
	j := gjson.New(jsonContent)
	fmt.Println(j.Get("name"))
	fmt.Println(j.Get("score"))
	
	// 创建json
	j2 := gjson.New(nil)
	j2.Set("name", "John")
	j2.Set("score", 99.5)
	fmt.Printf(
		"Name: %s, Score: %v\n",
		j2.GetString("name"),
		j2.GetFloat32("score"),
	)
	fmt.Println(j2.MustToJsonString())

	// struct转json
	type Me struct {
		Name  string `json:"name"`
		Score int    `json:"score"`
	}
	me := Me{
		Name:  "john",
		Score: 100,
	}
	j3 := gjson.New(me)
	fmt.Println(j3.Get("name"))
	fmt.Println(j3.Get("score"))
	// 转换回Struct
	Me2 := new(Me)
	if err := j.ToStruct(Me2); err != nil {
		panic(err)
	}
	fmt.Printf(`%+v`, Me2)
	fmt.Println()

	// 格式转换
	fmt.Println("JSON:")
	fmt.Println(j3.MustToJsonString())
	fmt.Println("======================")

	fmt.Println("XML:")
	fmt.Println(j3.MustToXmlString("document"))
	fmt.Println("======================")

	fmt.Println("YAML:")
	fmt.Println(j3.MustToYamlString())
	fmt.Println("======================")

	fmt.Println("TOML:")
	fmt.Println(j3.MustToTomlString())

10.4gmd5

  • MD5算法
示例
	p := fmt.Println
	// md5加密
	p(gmd5.MustEncrypt("123456"))

10.5类型转换

gconv

gf框架提供了非常强大的类型转换包gconv,可以实现将任何数据类型转换为指定的数据类型,对常用基本数据类型之间的无缝转换,同时也支持任意类型到struct对象的转换。由于gconv模块内部大量使用了断言而非反射(仅struct转换使用到了反射),因此执行的效率非常高。

Struct转换

项目中我们经常会遇到大量struct的使用,以及各种数据类型到struct的转换/赋值(特别是json/xml/各种协议编码转换的时候)。为提高编码及项目维护效率,gconv模块为各位开发者带来了极大的福利,为数据解析提供了更高的灵活度。

示例
	i := 123.456
	fmt.Printf("%10s %v\n", "Int:",        gconv.Int(i))
	fmt.Printf("%10s %v\n", "Int8:",       gconv.Int8(i))
	fmt.Printf("%10s %v\n", "Int16:",      gconv.Int16(i))
	fmt.Printf("%10s %v\n", "Int32:",      gconv.Int32(i))
	fmt.Printf("%10s %v\n", "Int64:",      gconv.Int64(i))
	fmt.Printf("%10s %v\n", "Uint:",       gconv.Uint(i))
	fmt.Printf("%10s %v\n", "Uint8:",      gconv.Uint8(i))
	fmt.Printf("%10s %v\n", "Uint16:",     gconv.Uint16(i))
	fmt.Printf("%10s %v\n", "Uint32:",     gconv.Uint32(i))
	fmt.Printf("%10s %v\n", "Uint64:",     gconv.Uint64(i))
	fmt.Printf("%10s %v\n", "Float32:",    gconv.Float32(i))
	fmt.Printf("%10s %v\n", "Float64:",    gconv.Float64(i))
	fmt.Printf("%10s %v\n", "Bool:",       gconv.Bool(i))
	fmt.Printf("%10s %v\n", "String:",     gconv.String(i))

	fmt.Printf("%10s %v\n", "Bytes:",      gconv.Bytes(i))
	fmt.Printf("%10s %v\n", "Strings:",    gconv.Strings(i))
	fmt.Printf("%10s %v\n", "Ints:",       gconv.Ints(i))
	fmt.Printf("%10s %v\n", "Floats:",     gconv.Floats(i))
	fmt.Printf("%10s %v\n", "Interfaces:", gconv.Interfaces(i))

	fmt.Println("##############")
	// struct和map转换
	type User struct {
		Uid  int    `c:"uid"`
		Name string `c:"name"`
	}
	// 对象
	m := gconv.Map(User{
		Uid      : 1,
		Name     : "john",
	})
	fmt.Println(m)

	fmt.Println("##############")
	user := (*User)(nil)
	err := gconv.Struct(m, &user)
	if err != nil {
		panic(err)
	}
	g.Dump(user)
  • 15
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值