# Beerus

# 安装依赖

go get github.com/Beerus-go/Beerus@v1.1.9

# 架构组成

  • http直接使用的go自带的net/http包,在此基础上扩展了路由的管理方式
  • WebSocket 在http的基础上做的扩展,实现了协议升级,消息监听,报文解析,消息发送 等功能
  • udp 基于net包开发的,做了一个简单的二次封装

除了上面介绍的这些依赖的底层的技术,还扩展了很多功能,大家可以看下面的文档慢慢了解

# HTTP服务

# 创建路由

  • 直接调用route 下面的函数即可,需要什么请求方式 就调用什么请求方式的函数
  • 第一个参数是路由的URL,就是前端请求的地址
  • 第二个参数是 一个函数,当前端请求这个URL,服务端就会自动执行这个函数
  • 第二个参数(函数) 必须有返回值,支持struct,map,数组,beerus会自动将返回值转成Json响应给前端,这里为了演示方便就用的map
  • 如果你不喜欢这种响应方式,或者你不打算在你的项目里采用JSON来跟前端交互,那么可以关闭JSON模式,这个后面会讲
func CreateRoute() {
	// post route example
    route.POST("/example/post", func (req commons.BeeRequest, res commons.BeeResponse) map[string]string {
        
        msg := make(map[string]string)
		msg["msg"] = "success"
		return param
    })

    // get route example
    route.GET("/example/get", func (req commons.BeeRequest, res commons.BeeResponse) map[string]string{
    
        msg := make(map[string]string)
		msg["msg"] = "success"
		return param
    })
}

# 传统的方式获取参数

// 可以获取到前端传来的参数,不限请求方式
req.FormValue("参数name")

// 可以获取到前端传来的多个同名参数,返回一个数组,只对:普通表单,get请求 生效,formData是无效的
req.FormValues("参数name")

// 可以获取到前端传来的请求头,不限请求方式
req.HeaderValue("请求头name")

// 可以获取前端传来的文件,仅限 form-data
req.GetFile("参数name")

// 可以获取到以json传参的 json字符串
req.Json

# 直接将参数提取到struct

首先需要定一个struct,匹配规则如下

  • 如果tag里面设置了field属性,会优先根据field去匹配,如果没匹配到 那么会根据字段名再匹配一次
  • 支持的类型,都在下面的示例中了
  • commons.BeeFile 类型 只对 formdata生效
  • []string 只对:普通表单,json,get请求 生效,formData是无效的
// DemoParam If you have a struct like this, and you want to put all the parameters from the request into this struct
type DemoParam struct {
	// You can customize any field
	// the name of the field must be exactly the same as the name of the requested parameter, and is case-sensitive
	TestStringReception    string  `field:"testStringReception"`
	TestIntReception       int     `field:"testIntReception"`
	TestInt64Reception     int64   
	TestUintReception      uint    
	TestUint32Reception    uint32  
	TestUint64Reception    uint64  
	TestFloatReception     float32 
	TestStringRegReception string  
	TestBoolReception      bool

	TestBeeFileReception   commons.BeeFile

	TestArrayReception []string
}

然后,将这个struct作为 路由函数的参数

  • 路由函数的参数:个数不限制,可以只设置一个struct,也可以设置多个struct,也可以像示例中这样 跟request,response混用
// 注意看第一个参数
route.POST("/example/post", func(param DemoParam, req commons.BeeRequest, res commons.BeeResponse) map[string]string {

	println(param.TestStringReception)
	println(param.TestIntReception)
	println(param.TestInt64Reception)
	println(param.TestFloatReception)
	println(param.TestUintReception)
	println(param.TestUint64Reception)
	println(param.TestBoolReception)

	msg := make(map[string]string)
	msg["msg"] = "success"
	return param
})

也可以手工提取,调用 params.ToStruct函数 将req里面的参数全部提取到struct

  • 第一个参数是req
  • 第二个参数是 struct的指针

当执行完 params.ToStruct函数,param里就会有数据了

param := DemoParam{}

// Extraction parameters, Generally used in scenarios where verification is not required or you want to verify manually
params.ToStruct(req, &param)

# 启动服务

需要手动调用一下 创建路由的函数

func main() {    
    // Interceptors, routes, etc. Loading of data requires its own calls
    routes.CreateRoute()

    // Listen the service and listen to port 8080
    beerus.ListenHTTP(8080)
}

服务启动后,前端即可正常访问,访问方式: http://ip:port/路由地址

# 参数验证

想要实现自动验证参数,必须使用将参数提取到struct的方式来接收参数,验证方法也很简单,看如下示例

  • notnull: 设置成true,说明这个参数不可以为空
  • reg: 自己写正则进行验证,当不匹配时说明没通过验证
  • max: 字段的最大取值
  • min: 字段的最小取值
  • msg: 当验证没通过时,返回的提示消息
  • 其中: notnull,reg 仅对 string有效,max,min 仅对数字类型的字段有效
  • routes: 用来设置 这个验证对那些路由有效,多个用逗号分割,支持*通配符
type DemoParam struct {
	// You can customize any field
	// the name of the field must be exactly the same as the name of the requested parameter, and is case-sensitive
	TestStringReception    string  `notnull:"true" msg:"TestStringReception Cannot be empty" routes:"/example/put"`
	TestIntReception       int     `max:"123" min:"32" msg:"TestIntReception The value range must be between 32 - 123" routes:"/example/post"`
	TestInt64Reception     int64   `max:"123" min:"32" msg:"TestInt64Reception The value range must be between 32 - 123"`
	TestUintReception      uint    `max:"123" min:"32" msg:"TestUintReception The value range must be between 32 - 123"`
	TestUint32Reception    uint32  `max:"123" min:"32" msg:"TTestUint32Reception The value range must be between 32 - 123"`
	TestUint64Reception    uint64  `max:"123" min:"32" msg:"TestUint64Reception The value range must be between 32 - 123"`
	TestFloatReception     float32 `max:"123" min:"32" msg:"TestFloatReception The value range must be between 32 - 123"`
	TestStringRegReception string  `reg:"^[a-z]+$" msg:"TestStringRegReception Does not meet the regular"`
}

配置完上面这些之后,参数验证就自动生效了,如果验证没有通过,会返回一个json消息给前端

{"code":1128, "msg":"你在验证tag中设置的msg"}

# 异常处理机制

  • 在开发中,我们会经常给前端一些错误提示信息,按照常规的做法,我们需要在if里面 手工返回提示信息,但是这种写法不太优雅
  • 我们可以利用go里面的error来实现 错误提示
  • 给路由函数设置第二个返回值,类型必须是error
// post route example
route.POST("/example/post", func (req commons.BeeRequest, res commons.BeeResponse) (map[string]string, error) {

	if xxx {
		return nil, errors.New("错误提示信息")
	}
	
	msg := make(map[string]string)
	msg["msg"] = "success"
	return param, nil
})

# 文件下载功能

// Example of file download
route.GET("/downLoad/file", func(req commons.BeeRequest, res commons.BeeResponse) string {
	file, err := ioutil.ReadFile("/Users/yeyu/Downloads/goland-2021.2.4.dmg")
	if err == nil {

	}
	// 将文件写入客户端
	res.SendStream("goland.dmg", file)

	// 返回这个常量即可
	return web.Download
})

# 关闭JSON模式

关闭方式很简单, 只需要在创建路由前,加入以下代码即可

route.JsonMode = false

JSON模式一旦关闭,自动校验功能将会失效,因为beerus无法预知你打算用什么类型给前端响应,所以就直接把这个权限交给开发者了

  • 在你的路由里面,加上如下代码即可
// 调用params.Validation 函数进行验证,第二个参数是提取到了请求参数的 struct的指针
var result = params.Validation(req, &param)
// 当返回的不是SUCCESS,就说明验证没通过
if result != params.SUCCESS {
	// 这里可以自己选择合适的 SendXXX 函数
    res.SendErrorMsg(1128, result)
    return
}
  • 路由函数不可以有返回值,必须调用res里面的SendXXX函数 给前端响应数据
// post route example
route.POST("/example/post", func (req commons.BeeRequest, res commons.BeeResponse) {
	// 返回纯文本给客户端
	res.SendText("text")
})
  • 响应数据

我们提供了以下几个函数,来实现数据响应,你可以跟自己的需求,选择合适的函数

// 返回json给客户端
res.SendJson("json")

// 返回纯文本给客户端
res.SendText("text")

// 返回html页面给客户端
res.SendHtml("html text")

// 返回文件流给客户端
res.SendStream("filename", []byte)

// 返回其他自定义content-type的数据给客户端
res.SendData("data")

# 拦截器

  • 创建拦截器,跟创建路由特别像,直接调用 route.AddInterceptor函数即可
  • 第一个参数是 要拦截的路由路径,支持 * 通配符
  • 第二个参数是一个函数,里面可以实现拦截逻辑
  • 如果予以放行,直接返回true,否则返回false

注意点:

如果你在拦截器里返回false,那么务必要调用res.SendXXX 函数给前端一个响应,不然本次请求会一直阻塞到超时,对性能造成影响

// 需要在main函数里调用一下这个函数
func CreateInterceptor() {
	route.AddInterceptor("/example/*", loginInterceptorBefore)
}

func loginInterceptorBefore(req *commons.BeeRequest, res *commons.BeeResponse) bool {
	res.SetHeader("hello", "hello word").SetHeader("hello2", "word2")

	log.Println("exec interceptor")
	return true
}

# 会话管理

需要先创建一个session

  • Secret: 加密秘钥,长度必须=32
  • InitializationVector: 初始化常量,长度必须=16
  • Timeout: 有效时长,单位毫秒
session := new(commons.BeeSession)
session.Secret = "12345678abcdefgh09876543alnkdjfh"
session.InitializationVector = "12345678qwertyui"
session.Timeout = 3000

然后可以用session创建token

  • 将token给前端,前端每次请求都带回来即可
demo := Demo{}
demo.Name = "Beerus"
demo.Age = 18
demo.Height = 180

token, err := session.CreateToken(demo)

将token还原成原来的数据

demo2 := Demo{}
err = session.RestoreToken(token, &demo2)

原理介绍

# WebSocket 服务

# 创建路由

  • 这里为了偷懒只用了三个函数,实际操作中可以 每个路由对应三个不同的函数
  • 第一个参数session: 里面就两个元素,一个ID,一个 SendString,Send 函数
  • 第二个参数msg: 是客户端发来的消息,直接用即可
// CreateWebSocketRoute Creating websocket routes
func CreateWebSocketRoute() {
	wroute.AddWebSocketRoute("/ws/test", onConnection, onMessage, onClose)
	wroute.AddWebSocketRoute("/ws/test2", onConnection, onMessage, onClose)
}

// In order to save time, only three functions are used below. In practice, you can configure a set of functions for each route

func onConnection(session *wparams.WebSocketSession, msg string) {
	println(msg + "-------------------------------")
	session.SendString("connection success")
}

func onMessage(session *wparams.WebSocketSession, msg string) {
	println(msg + "-------------------------------")
	session.SendString("I got the message.")
}

func onClose(session *wparams.WebSocketSession, msg string) {
	println(msg + "-------------------------------")
}

# 启动服务

需要手动调用一下 创建路由的函数,启动的服务依然是HTTP服务

func main() {    
    // Interceptors, routes, etc. Loading of data requires its own calls
    routes.CreateWebSocketRoute()

    // Listen the service and listen to port 8080
    beerus.ListenHTTP(8080)
}

启动后 前端就可以正常的发起WebSocket通讯了,连接方式: ws://ip:port/路由路径

# UDP服务

# 启动服务

  • 不存在路由的概念,只有一个函数用来接收数据
  • 第一个参数就是 这个接收数据的函数
  • 第二个参数是结束符,用来标识数据读到哪 才算一个消息读完了
  • 第三个参数就是端口号
func main() {

	// Listening to a UDP service
	// The first parameter is the handler
	// The second parameter is the data separator
	// The third parameter is the port
	beerus.ListenUDP(updHandler, []byte("|"), 8080)

}

func updHandler(data []byte) {
	// data is the data you received
	println(util.BytesToString(data))
}

接收到数据以后,直接转成string即可

# 数据库操作

点击此处->跳转到数据库操作