Go-Spring 学习笔记一

记录版本

  • Go-Spring v1.1.0-rc2版本,项目地址:https://github.com/go-spring/go-spring

安装

# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2

最小化Web应用

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	// 这里必须引入web starter, 也可改为 "github.com/go-spring/starter-echo"
	_ "github.com/go-spring/starter-gin"
	"log"
)

func init() {
	// 创建一个GET方法的访问端点/hello-go-spring, 同Java SpringBoot 的 @GetMapping 注解
	// 访问http://127.0.0.1:8080/hello-go-spring 将看到返回{"code": 200, "msg": "SUCCESS", "data": "Hello Go-Spring!"}
	gs.GetMapping("/hello-go-spring", func(ctx web.Context) {
		// 回调形式返回结果
		ctx.JSON(web.SUCCESS.Data("Hello Go-Spring!"))
	})
}

func main() {
	// 启动应用程序, 同Java SpringBoot 的 SpringApplication.run(...)
	log.Fatal(gs.Run())
}

Go-Spring常用语法

1. 对象注入spring bean

package main

import (
	"fmt"
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/gs/cond"
	"log"
)

type testA struct {
	path string `value:"${GOPATH}"`
}

func (t *testA) String() string {
	return "testA: " + t.path
}

type testB struct {
	a *testA
}

func (t *testB) String() string {
	return "testB: " + t.a.String()
}

func newTestB(a *testA) *testB {
	return &testB{a: a}
}

func init() {
	// 创建bean, 并将bean设置为primary
	// Primary() 方法 => 将bean设置为该类型的默认bean
	gs.Object(new(testA)).Primary().Init(func(a *testA) {
		fmt.Println("1." + a.String())
	})
	
	// gs.Provide方法将对象注入spring bean中, Provide方法与Object方法相似, Provide方法还支持方法创建bean的形式, 参见示例6
	// Name(string) 方法 => 更改bean的名称
	gs.Provide(new(testA)).Name("testA2").Init(func(a *testA) {
		a.path = "newPath"
		fmt.Println("2." + a.String())
	})
	
	// 创建bean并绑定销毁方法
	// Destroy(func(interface{})) => 添加bean销毁时的方法
	gs.Object(new(testA)).Name("testA3").Destroy(func(a *testA) {
		// 程序停止时将打印该内容
		fmt.Println("destroy 3." + a.String())
	}).Init(func(a *testA) {
		fmt.Println("3." + a.String())
	})
	
	// 指定创建bean的条件, 与Java Spring @Condition系列注解相同
	// On(cond.Condition) => 添加创建bean的条件. 多个条件使用 cond.OnBean("bean1").OnBean("bean2").OnProperty(..) 方式创建
	gs.Object(new(testA)).Name("testA4").On(cond.OnBean("testA3")).Init(func(a *testA) {
		fmt.Println("4." + a.String())
	})
	
	// 设置bean的排序顺序, 优先级从小到大, 同Java Spring @Order注解
	// Order(int) => 设置bean的排序顺序
	gs.Object(new(testA)).Name("testA5").Order(1).Init(func(a *testA) {
		fmt.Println("5." + a.String())
	})
	
	// 将方法返回的对象注入至spring bean中, 参数从spring容器中获取
	// 当有多个依赖类型且无Primary时, 需指定bean名称,例如: gs.Privide(newTestB, "testA2")...
	gs.Provide(newTestB).Init(func(b *testB) {
		fmt.Println("6." + b.String())
	})
}

func main() {
	log.Fatal(gs.Run())
}

2.创建属性监听器

package main

import (
	"fmt"
	"github.com/go-spring/spring-core/gs"
	"log"
)

type user struct {
	// 需与配置文件字段名相同
	Name string `value:"${name}"`
	Age int `value:"${age}"`
}

func init() {
	// 创建属性监听器, 当属性装载后会执行自定义方法, 方法签名 func(interface{})
	gs.OnProperty("GOPATH", func(s string) {
		fmt.Println("4." + s)
	})
    // 设置环境变量
	gs.Setenv("user.name", "dragons")
	gs.Setenv("user.age", "24")
    // 设置属性(与环境变量的区别在于env是进程全局生效, 而Property仅在一个web容器中生效)
    gs.Property("xxx", "value")
    
	// 创建属性监控器, 属性绑定struct, struct的value需与属性名一致
	gs.OnProperty("user", func(u user) {
		fmt.Printf("5.%v\n", u)
	})
	/*
	创建属性监控器, 数组结构
	config/application.yml
	users:
	  - name: dragons
	    age: 24
	  - name: xxj
	    age: 16
	*/
	gs.OnProperty("users", func(u []user) {
		fmt.Printf("6.%v\n", u)
	})
}

func main() {
	log.Fatal(gs.Run())
}

Web应用常用语法

1.创建Web端点(Path)

1.1 Mapping方式

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"

	// 引入web引擎starter
	_ "github.com/go-spring/starter-gin"
	"log"
)

func init() {
    // gs.*Mapping(path, func(web.Context))
	// GET 请求
	gs.GetMapping("/get", func(ctx web.Context) {
		// 获取参数-------------------------------------
		// 获取请求头参数
		ctx.GetHeader("user-agent")
		// 获取query参数
		ctx.QueryParam("")
		// 获取raw参数
		_, _ = ctx.GetRawData()

		// 返回结果-------------------------------------
		// 返回json结构对象或map
		/**
		{"code": 200, "msg": "SUCCESS"}
		*/
		ctx.JSON(web.SUCCESS)
		ctx.JSON(map[string]interface{}{"code": 200, "msg": "SUCCESS"})
		ctx.JSON(struct {
			Code int `json:"code"`
			Msg string `json:"msg"`
		}{200, "SUCCESS"})
		// 返回原始串
		/* message */
		ctx.String("message")
		// 返回文件内容
		/**
		name: dragons
		age: 24
		*/
		ctx.File("./config/application.yml")
	})
	
	// POST 请求
	gs.PostMapping("/post", func(ctx web.Context) {
		// ctx方法与GET请求一致
		// 获取表单数据 from-data
		ctx.FormValue("name")
	})
	
	// PUT 请求
	gs.PutMapping("/put", func(ctx web.Context) {
	})
	
	// DELETE 请求
	gs.DeleteMapping("/delete", func(ctx web.Context) {
	})
}

func main() {
	log.Fatal(gs.Run())
}

1.2 Binding方式

package main

import (
	"context"
	"github.com/go-spring/spring-core/gs"
	// 引入web引擎starter
	_ "github.com/go-spring/starter-gin"
	"log"
)

// 示例Bind请求类型
type req struct {
	/**
	常用tags:
		json => 输入、输出 json 结构时的参数名称
		xml => 输入、输出 xml 结构时的参数名称
		bson => 输入、输出 bson 结构时的参数名称
		yaml => 输入、输出 yaml 结构时的参数名称
		form => query、form-data 参数名称
		...
	*/
	Name string `json:"name" form:"name" xml:"name" yaml:"name" bson:"name"`
}

// 示例Bind返回类型
type resp struct {
	Code int `json:"code"`
	Msg string `json:"msg"`
	Data string `json:"data"`
}

func init() {
	// gs.*Binding(path, func(context.Context, interface{}) interface{})
	// GET 请求
	// 其中Binding的fn参数必须是func且只能有两个参数一个返回值, 第一个参数是context.Context而不是web.Context, 第二个参数返回值必须为指针类型
	gs.GetBinding("/get", func(ctx context.Context, r *req) *resp {
		return &resp{200, "请求成功", r.Name}
	})
	
	// POST 请求
	gs.PostBinding("/post", func(ctx context.Context, r *req) *resp {
		return &resp{200, "请求成功", r.Name}
	})
	
	// PUT 请求
	gs.PutBinding("/put", func(ctx context.Context, r *req) *resp {
		return &resp{200, "请求成功", r.Name}
	})
	
	// DELETE请求
	gs.DeleteBinding("/delete", func(ctx context.Context, r *req) *resp {
		return &resp{200, "请求成功", r.Name}
	})
}

func main() {
	log.Fatal(gs.Run())
}

1.3 Handle方式

package main

import (
	"github.com/go-spring/spring-base/util"
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"

	// 引入web引擎starter
	_ "github.com/go-spring/starter-gin"
	"log"
)

// 测试handler, 实现web.Handler接口
type testHandler struct {
}

func (t *testHandler) Invoke(ctx web.Context) {
	ctx.JSON(web.SUCCESS)
}

func (t *testHandler) FileLine() (file string, line int, fnName string) {
	return util.FileLine(t.Invoke)
}

func init() {
	// GET 请求
	// 传入一个实现web.Handler接口的对象
	// 与 gs.*Mapping区别在于web.Handler接口多了一个FileLine方法, 用于获取用户函数的文件名、行号以及函数名称
	gs.HandleGet("/get", &testHandler{})
	
	// POST 请求
	gs.HandlePost("/post", &testHandler{})
	
	// PUT 请求
	gs.HandlePut("/put", &testHandler{})
	
	// DELETE 请求
	gs.HandleDelete("/delete", &testHandler{})
}

func main() {
	log.Fatal(gs.Run())
}

配置应用

多环境配置(以yml文件为例)

1. application-dev.yml

user:
  name: dragons
  age: 24

2. application-test.yml

user:
  name: xxl
  age: 14

3. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	"log"
)

type user struct {
	Name string `value:"${name}"`
	Age int `value:"${age}"`
}

func init() {
	// 设置配置目录
    gs.Setenv("GS_SPRING_CONFIG_LOCATIONS", "config/")
	// 设置dev环境配置值, 两种方式均可
    gs.Setenv("GS_SPRING_PROFILES_ACTIVE", "dev")
	// gs.Setenv("spring.profiles.active", "dev")
	gs.OnProperty("user", func(u user) {
		gs.GetMapping("/user", func(ctx web.Context) {
			ctx.JSON(web.SUCCESS.Data(u))
		})
	})
}

func main() {
	log.Fatal(gs.Run())
}

配置方式一:Tag value + autowire注入

1. config/application.yml

# 默认情况下自动加载项目路径下的 conifg/application.yml 或 config/application.properties 文件配置
# 这里以application.yml为例, config/application.yml 文件
name: dragons
age: 24

2. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	"log"
)

// 定义model
type user struct {
    // 加载环境变量中的 name 属性, ":=" 语法用于设置默认值, 同Java Spring @Value("${name:}")
	Name string `json:"name" value:"${name:=}"`
    // 加载环境变量中的 age 属性
	Age int `json:"age" value:"${age:=0}"`
}

// 定义控制器
type userController struct {
    // 自动注入*user实例, 也可将?改为具体beanName, 例如当前测试 ? 可改为 user
	u *user `autowire:"?"`
}

// 定义控制器端点方法, 控制器端点方法签名为 func(..) ...(ctx web.Context)
func (c *userController) echoUser(ctx web.Context) {
    // 这里直接返回model对象, 指针类型与值类型最终都将转为值类型
	ctx.JSON(c.u)
}

func init() {
	// 创建user类型的bean实例, 自动装载user配置
	gs.Object(new(user))
	// 创建userController控制器bean实例, 并在控制器实例创建后添加初始化方法 	
    // 访问http://127.0.0.1:8080/echo-user 将看到返回{"name": "dragons", "age": 24}
	gs.Object(new(userController)).Init(func(c *userController) {
        // 初始化web端点/echo-user, 将该端点绑定至userController控制器的echoUser方法上
		gs.GetMapping("/echo-user", c.echoUser)
	})
}

func main() {
	log.Fatal(gs.Run())
}

配置方式二: Tag value 属性直接注入

1.config/application.yml

users:
  - name: dragons
    age: 24
  - name: xxy
    age: 14

2. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	"log"
)

// 定义model
type user struct {
	// 设置tag value对应的配置名, users下的属性名为name, 因此这里填写${name}即可注入
	Name string `json:"name" form:"name" yaml:"name" value:"${name}"`
	// 设置tag value对应的配置名
	Age int `json:"age" form:"age" yaml:"age" value:"${age}"`
}

// 定义controller
type userController struct {
    // 使用application.yml中的users属性直接注入
	users []user `value:"${users}"`
}

// 控制器端点方法
func (c *userController) echoUsers(ctx web.Context) {
	ctx.JSON(c.users)
}

func init() {
	// 创建userController控制器bean实例, 并在控制器实例创建后添加初始化方法 	
	gs.Object(new(userController)).Init(func(controller * userController) {
         // 初始化web端点/echo-users, 将该端点绑定至userController控制器的echoUser方法上
		gs.GetMapping("/echo-users", controller.echoUsers)
	})
}

func main() {
	log.Fatal(gs.Run())
}

Go-Spring 学习笔记二

记录版本

  • Go-Spring v1.1.0-rc2,项目地址:https://github.com/go-spring/go-spring

安装

# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2
# 安装gorm starter
go get -u github.com/go-spring/starter-gorm@v1.1.0-rc2
# 安装gorm
go get -u github.com/jinzhu/gorm
# 安装 redis starter
# go-redis
go get -u github.com/go-spring/starter-go-redis@v1.1.0-rc2
# redigo
go get -i github.com/go-spring-starter-redigo@v1.1.0-rc2

Web 应用集成starter-gorm

1. 配置文件config/application.yml

# gorm starter mysql 默认配置项, 同spring.datasource
db:
  url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local

2. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	// 需要引入该模块自动装载默认的gorm, 若无需使用自动装载可自定义gorm bean
	_ "github.com/go-spring/starter-gorm/mysql"
	"github.com/jinzhu/gorm"
	"log"
)

// 定义model
/**
建表sql
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1460907264817729538 DEFAULT CHARSET=utf8
数据sql
INSERT INTO `user` VALUES(1, "dragons", 24, "521274311@qq.com")
 */
type user struct {
	// 主键ID
	Id int64 `json:"id" gorm:"primary_key"`
	Username string `json:"username"`
	Age int `json:"age"`
	Email string `json:"email"`
}

// TableName 获取表名方法, 默认复数表名, 这里表名与model名称相同需要实现Tabler接口
func (u *user) TableName() string {
	return "user"
}

// 定义控制器
type gormController struct {
	// 自动注入starter启动的*gorm.DB
	db *gorm.DB `autowire:"?"`
}

// 定义查询所有用户的端点方法
func (c *gormController) selectUsers(ctx web.Context)  {
	var users []user
	// 查询所有用户信息
	c.db.Model(&user{}).Find(&users)
	// 返回所有用户信息
	ctx.JSON(&users)
}

func init() {
	// 创建 gormController 控制器bean实例, 并在控制器实例创建后添加初始化方法
	// 访问 http://127.0.0.1:8080/users 将看到返回 [{"id": 1, "username", "dragons", "age": 24, "email": "521274311@qq.com"}]
	gs.Object(new(gormController)).Init(func(c *gormController) {
		// 添加Web端点/users, 绑定gormController.selectUsers方法
		gs.GetMapping("/users", c.selectUsers)
	})
}

func main() {
	log.Fatal(gs.Run())
}

Web 应用集成gorm多数据源

1.config/application.yml

# 这里自定义了一个配置项 dbs (可自行更改) 来配置多数据源, 其中name为bean名称, type为数据库类型(与gorm数据库类型名称一致)
dbs:
  - name: gorm-db1
    url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local
    type: mysql
  - name: gorm-db2
    url: root:password@tcp(host:3306)/db_name?charset=utf8mb4&parseTime=True&loc=Local
    type: mysql

2. 创建一个dbs.go文件用来单独注入*gorm.DB实例

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/jinzhu/gorm"
	"github.com/labstack/gommon/log"
	// 需要引入Mysql驱动
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

// 定义配置struct, 属性与配置文件相同
type DBConfig struct {
	Name string `value:"${name}"`
	Url string `value:"${url}"`
	Type string `value:"${type}"`
}

func init() {
    // 创建一个配置监听器, 当配置加载后将执行该方法
	gs.OnProperty("dbs", func(config []DBConfig) {
        // 遍历配置项
		for _, dbConfig := range config {
			db, err := CreateDB(dbConfig)
			if err != nil {
				log.Fatal(err)
			}
             // 将*gorm.DB注入Spring bean中
			gs.Object(db).Destroy(CloseDB).Name(dbConfig.Name)
		}
	})
}

func CreateDB(config DBConfig) (*gorm.DB, error) {
	return gorm.Open(config.Type, config.Url)
}

func CloseDB(db *gorm.DB) {
	log.Info("close gorm mysql")
	if err := db.Close(); err != nil {
		log.Error(err)
	}
}

3. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	"github.com/jinzhu/gorm"
	"log"
)

// 定义model
/**
建表sql
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1460907264817729538 DEFAULT CHARSET=utf8
数据sql
INSERT INTO `user` VALUES(1, "dragons", 24, "521274311@qq.com")
 */
type user struct {
	// 主键ID
	Id int64 `json:"id" gorm:"primary_key"`
	Username string `json:"username"`
	Age int `json:"age"`
	Email string `json:"email"`
}

// TableName 获取表名方法, 默认复数表名, 这里表名与model名称相同需要实现Tabler接口
func (u *user) TableName() string {
	return "user"
}

// 定义控制器
type gormController struct {
	// 注入指定bean名称为gorm-db1的实例
    /*
    v1.1.0移除getBean方法, 属性注入阶段后不保留研数据, 若需多项db切换则需要提前注入多项db的bean,自行实现切换逻, 示例如下:
    type StructName struct {
		db1 *gorm.DB `autowire:"gorm-db1"`
		db2 *gorm.DB `autowire:"gorm-db2"`
	}
    */
	db *gorm.DB `autowire:"gorm-db1"`
}

// 定义查询所有用户的端点方法
func (c *gormController) selectUsers(ctx web.Context)  {
	var users []user
	// 查询所有用户信息
	c.db.Model(&user{}).Find(&users)
	// 返回所有用户信息
	ctx.JSON(&users)
}

func init() {
	// 创建 gormController 控制器bean实例, 并在控制器实例创建后添加初始化方法
	// 访问http://127.0.0.1:8080/users 将看到返回[{"id": 1, "username", "dragons", "age": 24, "email": "521274311@qq.com"}]
	gs.Object(new(gormController)).Init(func(c *gormController) {
		// 添加Web端点/users, 绑定gormController.selectUsers方法
		gs.GetMapping("/users", c.selectUsers)
	})
}

func main() {
	log.Fatal(gs.Run())
}

Web 应用集成starter-go-redis/starter-redigo

1. config/application.yml

# redis starter 配置项
redis:
  host: host
  port: 6379
  password: password
  database: 0

2. main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/redis"
	"github.com/go-spring/spring-core/web"
	_ "github.com/go-spring/starter-gin"
	"log"
	// 引入go-redis starter依赖
	_ "github.com/go-spring/starter-go-redis"
    // 引入redigo starter依赖
    // _ "github.com/go-spring/starter-redigo"
)

// 定义 redisController 控制器
type redisController struct {
	// 注入redis.Client bean, 注意redis为github.com/go-spring/spring-core/redis
	redisClient redis.Client `autowire:"?"`
}

// redis get 方法
func (c *redisController) get(ctx web.Context) {
	// 获取入参的key
	key := ctx.QueryParam("key")
	// redis get [key] 获取value
	value, err := c.redisClient.Get(ctx.Context(), key)
	if err != nil {
		ctx.JSON(web.ERROR.Error(err))
	} else {
		ctx.JSON(web.SUCCESS.Data(value))
	}
}

func init() {
	gs.Object(new(redisController)).Init(func(c *redisController) {
		gs.GetMapping("/redis/get", c.get)
	})
}

func main() {
	log.Fatal(gs.Run())
}

Go-Spring 学习笔记三

记录版本

  • Go-Spring v1.1.0-rc2

安装

# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# echo
go get -u github.com/go-spring/starter-echo@v1.1.0-rc2

1.过滤器

1.1 创建Web过滤器

main.go

package main

import (
	"errors"
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	// 引入spring web引擎
	// gin
	SpringGin "github.com/go-spring/spring-gin"
	// echo
	//SpringEcho "github.com/go-spring/spring-echo"
	// 这里使用SpringGin方式创建一个web容器, 因此引入web starter而不是具体某个web引擎的starter
	_ "github.com/go-spring/starter-web"
	"log"
)

// 定义token过滤器
type TokenFilter struct {
}

/**
	设置过滤器指定的path pattern范围, 这里设置为 /test2/* 即拦截 /test2/*类型的端点
	若不定义该方法则默认为全局拦截器
 */
func (f *TokenFilter) URLPatterns() []string {
	return []string{"/test2/*"}
}

// 实现 web.Filter 接口
func (t *TokenFilter) Invoke(ctx web.Context, chain web.FilterChain) {
	token :=  ctx.QueryParam("token")
	if token != "abcde" {
		ctx.JSON(web.ERROR.Error(errors.New("invalid token")))
	} else {
		chain.Next(ctx)
	}
}

func init() {
	// 使用SpringGin创建web容器, 使用该方式可创建多http server
	webContainer := SpringGin.NewContainer(web.ContainerConfig{
		Port:     8080,
		BasePath: "/",
	})
	// 为web容器添加token过滤器
	webContainer.AddFilter(&TokenFilter{})
	// 若直接使用默认容器则使用如下方法添加过滤器
	// gs.Object(&TokenFilter{}).Export((*web.Filter)(nil))

	// 注入容器并设置导出接口
	gs.Object(webContainer).Export((*web.Container)(nil))
	
	/*
		访问 http://127.0.0.1:8080/test/t1 将不会经过token拦截器, 返回:
			{
				"code": 200,
				"msg": "SUCCESS"
			}
		访问 http://127.0.0.1:8080/test2/t1 将被token过滤器拦截, 返回:
			{
				"code": -1,
				"msg": "ERROR",
				"err": "/path/main.go:32: invalid userId"
			}
		访问 http://127.0.0.1:8080/test2/t1?token=abcde 将被token过滤器放行, 返回:
			{
				"code": 200,
				"msg": "SUCCESS"
			}
	*/
	gs.Object(new(testController)).Init(func(c *testController) {
		gs.GetMapping("/test/t1", c.echo)
		gs.GetMapping("/test2/t1", c.echo)
	})
}

// 定义测试控制器
type testController struct {
}

func (c *testController) echo(ctx web.Context) {
	ctx.JSON(web.SUCCESS)
}

func main() {
	log.Fatal(gs.Run())
}

在spring-gin v1.1.0-rc2版本中, 中间件不会直接拦截并返回结果, 而是将拦截器的结果与端点方法返回的结果组合返回, 这里修改源码文件github.com/go-spring/spring-gin/container.go的168行与174行即可解决(问题已反馈, 等到正式版应该不会有这种问题了)

// ...省略
func HandlerWrapper(fn web.Handler, wildCardName string, filters []web.Filter) []gin.HandlerFunc {
	var handlers []gin.HandlerFunc

	handlers = append(handlers, func(ginCtx *gin.Context) {
		if WebContext(ginCtx) == nil {
			NewContext(fn, wildCardName, ginCtx)
		}
	})
	
	for _, filter := range filters {
		f := filter
		handlers = append(handlers, func(ginCtx *gin.Context) {
			f.Invoke(WebContext(ginCtx), &ginFilterChain{ginCtx})
			ginCtx.Abort() // 168行之后添加该代码
		})
	}
	
	handlers = append(handlers, func(ginCtx *gin.Context) {
		fn.Invoke(WebContext(ginCtx))
		ginCtx.Abort() // 174行之后添加该代码
	})
	
	return handlers
}
// ...省略

1.2 替换默认Web日志过滤器

Go-Spring启动的Web容器会带有默认的日志过滤器,当我们不需要使用默认日志而自定义日志时,可以在创建容器时重新设置日志过滤器, 如下所示:

main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
	SpringGin "github.com/go-spring/spring-gin"
    // 仅使用starter web
	_ "github.com/go-spring/starter-web"
	"log"
	"time"
)

func init() {
    // 方式一
	// 创建gin容器, 并设置初始化方法
	gs.Object(SpringGin.NewContainer(web.ContainerConfig{
		Port: 8080,
		BasePath: "/",
	})).Init(func(container *SpringGin.Container) {
        // 直接使用内置的web.FuncFilter方法构建过滤器
		container.SetLoggerFilter(web.FuncFilter(func(ctx web.Context, chain web.FilterChain) {
            // 自定义日志信息
			startTime := time.Now()
			chain.Next(ctx)
			log.Printf("接口耗时:%v", time.Since(startTime))
		}))
        // 导出为web.Container类型(若不指定将无法检测到web容器)
	}).Export((*web.Container)(nil))
    
    // 方式二
    // 使用gs.Provide + 函数方式创建 web 容器, 创建容器过程中配置容器日志过滤器及其他信息
    //gs.Provide(func() web.Container {
    //	webContainer := SpringGin.NewContainer(web.ContainerConfig{
    //		Port:     8080,
    //		BasePath: "/",
    //	})
    //	webContainer.SetLoggerFilter(web.FuncFilter(func(ctx web.Context, chain web.FilterChain) {
    //		startTime := time.Now()
    //		chain.Next(ctx)
    //		log.Printf("接口耗时:%v", time.Since(startTime))
    //	}))
    //	return webContainer
    //})

	// 定义GET方法端点
	gs.GetMapping("/", func(ctx web.Context) {
		ctx.JSON(web.SUCCESS.Data("Hello Go-Spring!"))
	})
}

func main() {
	log.Fatal(gs.Run())
}

Go-Spring 学习笔记四

记录版本

  • Go-Spring v1.1.0-rc2版本,Git地址:https://github.com/go-spring/go-spring

安装

# 安装spring核心包
go get -u github.com/go-spring/spring-core@v1.1.0-rc2
# 安装web starter
# gin
go get -u github.com/go-spring/starter-gin@v1.1.0-rc2
# 安装grpc starter
go get -u github.com/go-spring/starter-grpc@v1.1.0-rc2
# 安装grpc server
go get -u github.com/go-spring/starter-grpc/server@v1.1.0-rc2
# 安装grpc client
go get -u github.com/go-spring/starter-grpc/client@v1.1.0-rc2

使用Grpc通信

1. 工程结构

|-- client 
	|-- main.go # 客户端启动文件
|-- server
	|-- main.go # 服务端启动文件
|-- proto # 存放proto文件目录
	|-- product.proto # 测试product grpc服务
	|-- pb # 存放生成的go grpc pb文件
		|-- product.pb.go # product service 生成的pb文件
|-- service # 服务实现
	|-- product.go # product 服务实现
|-- go.mod
...

2. 准备工作

go 开发 grpc 相关配置及依赖参考 https://www.cnblogs.com/baoshu/p/13488106.html,主要安装一个protoc可执行文件与protoc-gen-go依赖来自动生成pb代码

3. Protobuf文件

在 proto目录下创建文件product.proto文件如下

syntax = "proto3";

// 定义go文件生成路径
option go_package = "proto/pb";

// 定义生成service的包名
package product;

// 定义服务
service Product {
  // 定义rpc端点
  rpc Produce (ProductRequest) returns(ProductResponse) {}
}

// 定义Produce端点的请求参数
message ProductRequest {
  // 参数类型与参数名称
  string name = 1;
  int64 count = 2;
}

// 定义Produce端点的响应参数
message ProductResponse {
  // 参数类型与参数名称
  uint32 code = 1;
  string msg = 2;
  string items = 3;
}

4. 生成*.pb.go文件

在工程根目录下执行命令

# protoc我这里已经添加到环境变量了, 若为添加环境变量则需要写出protoc的全路径
protoc --go_out=plugins=grpc:./ ./proto/product.proto

5. Service 实现

在service目录下创建product.go文件

package service

import (
	context "context"
    // 引入生成的pb文件对应的模块
	"xxx/proto/pb"
	"strconv"
)

// 定义service 需要实现生成pb文件的ProductServer接口
type ProductService struct {
}

// pb.ProductServer接口实现
func (p *ProductService) Produce(ctx context.Context, request *pb.ProductRequest) (*pb.ProductResponse, error) {
	return &pb.ProductResponse{
		Code:  200,
		Msg:   "请求成功",
		Items: "Produced " + strconv.FormatInt(request.Count, 10) + " product named \"" + request.Name + "\"",
	}, nil
}

6. 服务端启动程序

server/main.go

package main

import (
	"github.com/go-spring/spring-base/log"
	"github.com/go-spring/spring-core/grpc"
	"github.com/go-spring/spring-core/gs"
    // 引入grpc server starter
	_ "github.com/go-spring/starter-grpc/server"
	"xxx/proto/pb"
	"xxx/service"
)

func init() {
    // 创建service.ProductService bean示例并设置初始方法
	gs.Object(new(service.ProductService)).Init(func(srv *service.ProductService) {
        // 添加grpc服务
        // gs.GrpcServer(string, *grpc.Server) 其中第一个参数为serviceName, 要求与生成的pb文件Service.Desc中的ServiceName参数值相等
		gs.GrpcServer("product.Product", &grpc.Server{
             // 服务注册方法
			Register: pb.RegisterProductServer,
             // 服务实现对象
			Service:  srv,
		})
	})
}

func main() {
    // 设置grpc访问端口
	gs.Property("grpc.server.port", 8081)
	log.Fatal(gs.Run())
}

7. 客户端启动程序

client/main.go

package main

import (
	"github.com/go-spring/spring-core/gs"
	"github.com/go-spring/spring-core/web"
    // 引入gin web starter, 若不需要提供web访问可以移除该依赖
	_ "github.com/go-spring/starter-gin"
    // 引入 grpc client starter
	_ "github.com/go-spring/starter-grpc/client"
	"xxx/proto/pb"
	"log"
)

func init() {
    // 配置product访问端点, 配置结构为 grpc.endpoint.xxx.address 其中xxx为自己定义的名称
	gs.Property("grpc.endpoint.product.address", "127.0.0.1:8081")
    // 1.gs.GrpcClient(interface{}, string)创建grpc客户端bean实例, 第二个参数的endpoint名称必须与上面引入的名称一致
    // 2.Init(..) bean初始化的方法
	gs.GrpcClient(pb.NewProductClient, "product").Init(func(client pb.ProductClient) {
         // 创建Web端点
		gs.GetMapping("/", func(ctx web.Context) {
             // 调用rpc服务端实现并获取结果
			products, err := client.Produce(ctx.Context(), &pb.ProductRequest{
				Name:  ctx.QueryParam("name"),
				Count: 10,
			})
             // 结果返回
			if err != nil {
				ctx.JSON(web.ERROR.Error(err))
			} else {
				ctx.JSON(products)
			}
		})
	})
}


func main() {
	log.Fatal(gs.Run())
}

参考资料