bee 工具 本是一个为了协助快速开发 beego 项目而创建的项目,
通过 bee 可以快速创建项目、实现热编译、开发测试以及开发完之后打包发布的一整套从创建、开发到部署的方案。
go get github.com/beego/bee
安装完之后,bee可执行文件 配置成环境变量。
bee version
===》 查看bee工具版本信息。
bee new 项目名
===》 创建一个beego项目。
bee api 项目名
===》 创建一个beego专做api的项目。
bee run
===》 热编译,自动运行go文件中的 main函数。
beego 默认会解析当前应用下的 conf/app.conf 文件。
通过这个文件你可以初始化很多 beego 的默认参数:
appname = drugDataCollect
httpaddr = "127.0.0.1"
httpport = 6666
autorender = false
copyrequestbody = true
runmode = dev
[dev]
mysqluser = "root"
mysqlpassword = "123456"
mysqlurls = "127.0.0.1"
mysqldb = "drug_store_data_collect"
redisaddr = "127.0.0.1"
redisport = "6379"
redispassword = ""
redisdb = 1
[prod]
mysqluser = "root"
mysqlpassword = "123456"
mysqlurls = "127.0.0.1"
mysqldb = "drug_store_data_collect"
[test]
mysqluser = "root123test"
mysqlpassword = "123456"
mysqlurls = "127.0.0.1"
mysqldb = "gin"
beego.AppConfig.String("mysqluser")
beego.AppConfig.Int("redisdb")
首先把conf/app.conf文件修改为 运行环境
prod
runmode = prod
方式一: 使用bee工具
- 打包到Linux系统中使用
bee pack -be GOOS=linux
- 打包到window系统中使用
bee pack -be GOOS=windows
然后将打包好的压缩包 传至服务器中 解压运行。
- 方式二: 使用go命令
go build
将生成的可执行文件 连同 项目配置文件conf 以及一些静态资源文件,一同上传到服务器中,运行可执行文件。
根路由必须以 ‘/’ 开头。
适合的路由处理风格: 一个url、一个结构体、多个请求方法
beego.Router("/api/pharmacy", &controllers.Pharmacy{})
beego.Router("/api/upfile", &controllers.UpLoadFile{})
beego.Router("/api/cash_machine", &controllers.Cash{})
适合的路由处理风格: 多个url、一个结构体、一个请求方法
// 通过 NewNamespace 创建路由组
user := beego.NewNamespace("/user",
beego.NSRouter("login", &controllers.User{}),
beego.NSRouter("register", &controllers.User{}),
beego.NSRouter("repassword", &controllers.User{}),
)
// 通过 NSNamespace 深层嵌套
group := beego.NewNamespace("/a",
beego.NSNamespace("/bb",
beego.NSNamespace("/ccc",
beego.NSRouter("/dddd", &controllers.Aaa{})))
)
// 路由组 钩子
group := beego.NewNamespace("/a",
// 可理解为:前置中间件,如果返回true则可以达到后面的路由,false 则拒绝访问后面的路由
beego.NSCond(func(ctx *context.Context) bool {
fmt.Println("NSCond")
return true
}),
//NSCond 之后的钩子
beego.NSBefore(func(ctx *context.Context) {
fmt.Println("NSBefore")
}),
beego.NSRouter("/dddd", &controllers.Aaa{})
)
// 所有的 路由组信息 都要在这里 进行注册
beego.AddNamespace(user,group)
beego.Router("/aaa", &controllers.Wtt{}, "get:Abc")
get的方式访问/aaa路由,对应的处理函数 不再是Wtt上的Get方法,而是被指定
为Wtt上的Abc方法。
beego.SetStaticPath("imgs", "static") //imgs是访问的路径前缀,第二个是工程目录下的静态文件目录
例如: 在工程目录下的静态文件static
中有一个有一个aaa文件夹
,aaa文件有一个abc.jpg的图片
,那个这个图片的访问路径是:/imgs/aaa/abc.png
package main
import (
"fmt"
"github.com/beego/beego/v2/server/web/context"
beego "github.com/beego/beego/v2/server/web"
)
func Test(ctx *context.Context) {
fmt.Println("我是 中间件")
ctx.Output.Body([]byte{'o', 'k'})
}
func main() {
beego.Any("*", Test) // 广角路由
beego.Run("localhost:8088")
}
beego.AutoRouter(&Test{})
type Test struct {
beego.Controller
}
//访问路径: /test/abc 请求方法:any
func (that Test) Abc() {
that.Ctx.Output.JSON(123, false, false)
}
- 引入beego 的 content包
中间件 在 beego中 叫 过滤器 函数
值得注意的是:
每个中间件都一个content类型的参数,一定要手动引入该包
“github.com/astaxie/beego/context”
如果通过 保存操作 自动引入的话,会引入官方的 content包,
因为 go 官方 的content的包 优先 第三方的 content 的包的引入。
- 注册中间件 的 三个参数
所有的 中间件 都要放到 InsertFilter 函数中,其有三个参数:参1: 中间件守卫的路由
参2: 决定中间件执行的时机
五个固定实参如下,分别表示不同的执行过程:
beego.BeforeStatic
==》静态地址之前
beego.BeforeRouter
==》寻找路由之前
beego.BeforeExec
==》找到动态路由之后,开始执行相应的 Controller 之前
beego.AfterExec
==》执行完 Controller 逻辑之后执行的过滤器
beego.FinishRouter
==》执行完所有逻辑之后执行的过滤
参3: 指定让哪个中间件工作
- 执行顺序
多个中间件守卫一个路由,其执行顺序:
先由InsertFilter函数的的第二个参数决定,
如果第二个参数一直 则 由于 InsertFilter函数的 先后顺序 决定:
beego.InsertFilter("/aaa",beego.BeforeRouter, 中间件1)
beego.InsertFilter("/aaa",beego.BeforeRouter, 中间件2)
注意: 如果 任何一个 过滤器 中有向前端 返回数据 的动作, 则该 过滤器函数 执行周期之后的 过滤器函数 就不再执行了。
import (
"fmt"
"github.com/astaxie/beego/context"
)
func Test(ctx *context.Context) {
fmt.Println("我是 中间件")
}
func Test111(ctx *context.Context) {
fmt.Println("我是 中间件111")
}
beego.InsertFilter("/test", beego.BeforeRouter, Test)
beego.InsertFilter("/test", beego.BeforeRouter, Test111)
// 正则路由
beego.InsertFilter("/*", beego.BeforeRouter, Test) // 所有请求
beego.InsertFilter("/user([0-9]+)", beego.BeforeRouter, Test) // ()里面中写入正则
// 路由表
{
beego.Router("/a", &Test{}, "post:Post1")
beego.InsertFilter("/*", beego.BeforeRouter, m)
beego.InsertFilter("/*", beego.BeforeRouter, m2)
}
// 中间件
{
// 中间件
func m(ctx *context.Context) {
// 设置 要传值的值
ctx.Input.SetData("key", "value")
fmt.Println("我是 中间件")
}
// 中间件2
func m2(ctx *context.Context) {
// 获取 传值的值
res := ctx.Input.GetData("key")
// 设置 要传值的值
ctx.Input.SetData("key", "value123")
fmt.Println("我是 中间件2")
}
}
// 路由处理函数
{
type Test struct {
beego.Controller
}
func (that Test) Post1() {
// 获取 传值的值
res := that.Ctx.Input.GetData("key")
fmt.Println(res)
that.Ctx.Output.JSON(res, false, false)
}
}
// 输出:
value
value123
// 路由表
{
beego.InsertFilter("/*", beego.BeforeRouter, m)
beego.InsertFilter("/*", beego.BeforeRouter, m2)
// 不论 对 前端是否有回复 动作,都可以回到这里
beego.InsertFilterChain("/*", func(next beego.FilterFunc) beego.FilterFunc {
return func(ctx *context.Context) {
fmt.Println(1111111111)
next(ctx)
fmt.Println(2222222222222)
}
})
}
// 中间件
{
// 中间件
func m(ctx *context.Context) {
fmt.Println("我是 中间件")
}
// 中间件2
func m2(ctx *context.Context) {
fmt.Println("我是 中间件2")
}
}
// 输出:
/*
1111111111
我是 中间件
我是 中间件2
2222222222222
*/
package main
import (
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/server/web/filter/cors"
)
func init() {
//InsertFilter是提供一个过滤函数
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
// 允许访问所有源
AllowAllOrigins: true,
// 可选参数"GET", "POST", "PUT", "DELETE", "OPTIONS" (*为所有)
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
// 指的是允许的Header的种类
AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
// 公开的HTTP标头列表
ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
// 如果设置,则允许共享身份验证凭据,例如cookie
AllowCredentials: true,
}))
}
对于每个请求,beego 都会采用单独的 goroutine 来处理
原理模拟解释:
beego.Run()
// 其Run方法的核心原理为下述代码:
for {
conn := 监听前端连接
go Hander() // 开启协程处理函数
}
基于 beego 的 Controller 结构体 设计,只需要匿名组合 beego.Controller 就可以了
package controllers
import (
"github.com/astaxie/beego"
)
type Airplane struct {
beego.Controller
}
func (that Airplane) GuguAirplaneChatAutodel() {
inData := struct {
Uid int `json:"uid" validate:"required"`
}{}
err := json.Unmarshal(that.Ctx.Input.RequestBody, &inData)
if err != nil {
tools.Failed(that.Ctx, "请求参数异常"+err.Error())
return
}
err = tools.Check.Struct(inData)
if err != nil {
tools.Failed(that.Ctx, err.Error())
return
}
friends := make([]models.AirplaneFriend, 0, 50)
err = db.DB().Where("uid = ? and deleted = 2 and user_know = 0", inData.Uid).Find(&friends).Error
if err != nil {
err := fmt.Errorf("异常:%v", err)
tools.Failed(that.Ctx, err.Error())
return
}
tools.Success(that.Ctx, friends)
}
这个函数会在 这些 Method 方法
之前
执行,用户可以重写(重写:在继承的结构体上 重名覆盖这个方法)这个函数实现类似用户验证之类。
这个函数是在执行完相应的 HTTP Method 方法
之后
执行的,默认是空,用户可以在子 struct 中重写这个函数,执行例如数据库关闭,清理数据之类的工作
如果用户请求的 HTTP Method 是 GET,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Get 请求。
如果用户请求的 HTTP Method 是 POST,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Post 请求。
如果用户请求的 HTTP Method 是 DELETE,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Delete 请求。
如果用户请求的 HTTP Method 是 PUT,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Put 请求.
重定向。url是目的地址。
中断当前方法的执行,直接返回该状态码,类似于CustomAbort。
Input 对象是针对 request(请求报文) 的封装,里面实现很多方便的方法,具体如下:
获取用户请求的协议,例如 HTTP/1.1
请求的 scheme,例如 “http” 或者 “https”
请求的域名,例如 beego.me
返回请求的端口,例如返回 8080
请求的 URL 地址,例如 /hi
返回请求用户的 IP,如果用户通过代理,一层一层剥离获取真实的 IP
返回用户代理请求的所有 IP
返回请求的 UserAgent,例如 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
判断当前请求是否 HTTPS 请求,是返回 true,否返回 false
判断当前请求是否 Websocket 请求,如果是返回 true,否返回 false
判断是否是 AJAX 请求,如果是返回 true,不是返回 false
获取那些在路由上 早早定义好 的参数:
// 路由
beego.Router("/aaa/bbb/:name", &controllers.MainController{})
// url: http://localhost/aaa/bbb/tom
// 控制器
res := c.Ctx.Input.Param(":name") // tom
获取Get方法的请求参数: http://localhost/aaa/bbb?name=tom
解析 请求体为 json 和 xml 格式的数据
在配置文件里设置copyrequestbody = true
, 否则 RequestBody 拿不到数据
// var m map[string]interface{} 用map也可以,但是语义性不强。
reqObj := struct {
Account string
Password string
}{}
err := json.Unmarshal(that.Ctx.Input.RequestBody, &reqObj)
if err != nil {
fmt.Println(err)
}
fmt.Println(reqObj)
解析 请求体为 form-data 和 x-www-form-urlencode 格式的数据
type user struct {
Id int `form:"-"` // 忽略Id字段(一:字段名小写开头,二:form 标签的值设置为 -)
Name interface{} `form:"username"` // 只接受 username 字段
Age int `form:"age"` // 只接受 age 字段
Email string // 只接受 Email 字段
}
u := user{}
// 解析请求体为 form-data 和 x-www-form-urlencode 格式的数据
err := that.ParseForm(&u)
if err != nil {
fmt.Println(err)
}
获取:
post:form-data
、x-www-form-urlencode
get:?之后的参数
注意:此方法无法获取 post的json格式的数据
返回相应的 header 信息,例如 Header(“Accept-Language”)
返回请求中的 cookie 数据,例如 Cookie(“username”),就可以获取请求头中携带的 cookie 信息中 username 对应的值
文件上传之后一般是放在系统的内存里面,
如果文件的 size 大于设置的缓存内存大小,那么就放在 临时文件 中,
默认的缓存内存是 64M
,可以通过在配置文件中通过如下设置:
maxmemory = 1<<22
相应的也可以在代码中如下设置:
// 单位B
beego.BConfig.MaxMemory = 123
// 单位B, 4530 B == 4.5 K
beego.BConfig.MaxUploadSize = 4530
与此同时,beego 提供了另外一个参数,MaxUploadSize来限制最大上传文件大小——如果你一次长传多个文件,那么它限制的就是这些所有文件合并在一起的大小。
默认情况下,MaxMemory应该设置得比MaxUploadSize小,这种情况下两个参数合并在一起的效果则是:
判断当前请求是否有文件上传,有返回 true,否返回 false
实质上是判断接收的数据是否是form-data
类型的,如果是,哪怕是文字数据也返回true。
获取**
form-data
** 类型的 文件数据
func (that UpLoadFile) Post() {
f, h, err := that.GetFile("filename")
id := that.GetString("id")
name := that.GetString("name")
if err != nil {
fmt.Println(err)
tools.Failed(that.Ctx, "文件获取失败")
return
}
defer f.Close()
// 文件名
fmt.Println(h.Filename)
// 文件大小,单位B, h.Size / 1024 = KB
fmt.Println(h.Size)
uuid := uuid.New() // uuid(32位的随机 数组or字母)是文件名不重复的一个保证
// 该路径上的涉及到的 文件夹 提前创建好
var path string = "static/upload/" + id + "-" + name + "-" + uuid + h.Filename
// 该方法是在 GetFile 的基础上实现了快速保存的功能
err = that.SaveToFile("filename", path)
if err != nil {
fmt.Println(err)
tools.Failed(that.Ctx, "文件上传失败")
} else {
// 将 静态路由 返回给 前端
tools.Success(that.Ctx, path)
}
return
}
// 多文件上传
func (that File) UploadFiles() {
resM := make(map[string]string, 10)
// this is core
allUpFiles := that.Ctx.Request.MultipartForm.File
for k, _ := range allUpFiles {
res := upload(that, k)
resM[k] = res
}
tools.Failed(that.Ctx, resM)
}
func upload(that File, filename string) string {
f, h, err := that.GetFile(filename)
if err != nil {
tools.Failed(that.Ctx, "文件获取失败")
return ""
}
defer f.Close()
uuid := uuid.New()
var path string = "static/upload/" + uuid + h.Filename
err = that.SaveToFile(filename, path)
if err != nil {
fmt.Println(err)
tools.Failed(that.Ctx, "文件上传失败")
}
ddr, err := beego.AppConfig.String("httpaddr")
if err != nil {
panic("配置文件中 httpaddr 字段提取异常")
}
port, err := beego.AppConfig.String("httpport")
if err != nil {
panic("配置文件中 httpport 字段提取异常")
}
path = ddr + ":" + port + "/" + path
return path
}
Output 是针对 Response(响应报文) 的封装,里面提供了很多方便的方法:
将 参数数据 放到响应体中,并返回给前端
把 Data 格式化为 Json,然后调用 Body() 输出数据到前端
设置输出的 header 信息,例如 Header(“Server”,“beego”)
将cookie写入到前端浏览器中,例如 Cookie(“name”,“tom”)
请转到我的GORM那一篇。
这里用 token + cookie 的顶替 session,
session的不足:
在main函数中写入:
log.UseLog()
package log
import "github.com/astaxie/beego/logs"
func UseLog() {
log := logs.NewLogger(10000) // 创建一个日志实例,参数为缓冲区的大小
defer log.Close()
// 设置配置文件
// filename 文件名(不指定路径,就生成在主目录); maxlines 最大行; maxsize 最大Size;
jsonConfig := `{
"filename" : "test.log",
"maxlines" : 1000,
"maxsize" : 10240
}`
log.SetLogger("file", jsonConfig) // 设置日志记录方式!!!:本地文件记录
log.SetLevel(logs.LevelDebug) // 设置可以 写入 日志 的 错误的 等级
log.EnableFuncCallDepth(true) // 输出log时能显示输出文件名和行号(非必须)
// 通过主动让代码 触发 一些有等级的错误 ,已方便测试 test.log 对错误 的记录
log.Emergency("Emergency")
log.Alert("Alert")
log.Critical("Critical")
log.Error("Error")
log.Warning("Warning")
log.Notice("Notice")
log.Informational("Informational")
log.Debug("Debug")
log.Flush() // 将日志从缓冲区读出,写入到文件
}
/*
log.SetLogger 是 设置日志记录方式:
beego框架之日志模块默认支持4种记录方式:
1. 终端输出(console) :这种方式一般用在开发环境下面,方便调试。
2. 本地文件(file) :这种方式一般用来保存常规日志,为生产环境中常用的方式。
3. 网络方式(network):这种方式可以用来将日志发送到指定服务器,一般可以用来根据日志触发事件等。
4. 发送邮件(email) :这种方式一般是将生产环境下比较重要的日志发送给相应的管理人员,以便及时发现和解决问题。
log.SetLevel 是 设置 可以被记录的错误的 等级的:
beego框架之日志模块等级定义在github.com/astaxie/beego/logs/log.go:(级别以此递减)
const (
LevelEmergency = iota // 紧急级别
LevelAlert // 报警级别
LevelCritical // 严重错误级别
LevelError // 错误级别
LevelWarning // 警告级别
LevelNotice // 注意级别
LevelInformational // 报告级别
LevelDebug // 除错级别
)
*/
在做 Web 开发的时候,经常需要页面跳转和错误处理,beego 这方面也进行了考虑,
通过 控制器 上的 Redirect 和 Abort 方法来进行 跳转 或 中断。
package tools
import (
"os"
"github.com/astaxie/beego"
"github.com/fatih/color"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
//DB 数据库变量
var DB *gorm.DB
//DBinit 初始化数据库
func DBinit() *gorm.DB {
arr1 := "mysql"
arr2 := beego.AppConfig.String("mysqluser") +
":" + beego.AppConfig.String("mysqlpassword") +
"@(" + beego.AppConfig.String("mysqlurls") + ")/" +
beego.AppConfig.String("mysqldb") +
"?charset=utf8mb4&parseTime=True&loc=Local"
// var arr3 string = "root:123456@(127.0.0.1)/drug_store_data_collect?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(arr1, arr2)
if err != nil {
color.Red("连接数据库失败", err)
os.Exit(3)
} else {
d := color.New(color.FgCyan, color.Bold)
d.Printf("mysql 连接成功 \n")
}
db.SingularTable(true) // 不允许给 表名 加复数形式
DB = db
return db
}
package tools
import (
"time"
"github.com/astaxie/beego"
"github.com/garyburd/redigo/redis"
)
var Pool *redis.Pool
func RedisInit() {
// 连接池
Pool = &redis.Pool{
//最大闲置连接数
MaxIdle: 20,
//最大活动连接数, 0==无穷
MaxActive: 1000,
//闲置连接的超时时间 (如果开辟的IO 100秒之内什么都不干,就断定为闲置)
IdleTimeout: time.Second * 100,
//定义获得连接的 函数
Dial: func() (redis.Conn, error) {
// return redis.Dial("tcp", "127.0.0.1:6379", redis.DialDatabase(1)) // 没有密码的情况
// return redis.Dial("tcp", "127.0.0.1:6379", redis.DialPassword("123456"), redis.DialDatabase(1)) // 有密码的情况
protocol := "tcp"
url := beego.AppConfig.String("redisaddr") + ":" + beego.AppConfig.String("redisport")
dbIndex, _ := beego.AppConfig.Int("redisdb")
return redis.Dial(protocol, url, redis.DialDatabase(dbIndex))
},
}
// defer Pool.Close() // 注意: 连接池要一直开着,关闭了 就从里面取不出 io了,就是去了 连接池的意义了。
}
// 使用的时候 直接 从池中 取出一个 IO
// conn := Pool.Get()
// defer conn.Close()
// conn.Do("set", arr1, arr2)
package tools
import (
"time"
"github.com/dgrijalva/jwt-go"
)
var jwtKey = []byte("wtt")
//Claims token结构体
type Claims struct {
Username string `json:"username"`
Password string `json:"password"`
jwt.StandardClaims
}
//GetToken 产生token 给前端
func GetToken(username, password string) (string, error) {
expireTime := time.Now().Add(1 * time.Hour) // 有效时间一个小时
claims := &Claims{
Username: username,
Password: password,
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireTime.Unix(),
IssuedAt: time.Now().Unix(),
Issuer: "GoGin",
Subject: "wtt",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
return tokenString, err
}
//ParseToken 解析前端传过来的 token
func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
claims := &Claims{}
arr1 := tokenString
arr2 := claims
arr3 := func(token *jwt.Token) (i interface{}, err error) {
return jwtKey, nil
}
token, err := jwt.ParseWithClaims(arr1, arr2, arr3)
return token, claims, err
}
package tools
import (
"fmt"
"image/color"
"github.com/astaxie/beego/context"
"github.com/garyburd/redigo/redis"
"github.com/mojocn/base64Captcha"
)
type CaptchaResult struct {
Id string `json:"id"`
Base64Blob string `json:"base_64_blob"`
VerifyValue string `json:"code"`
}
// 配置验证码信息
var captchaConfig = base64Captcha.DriverString{
Height: 50,
Width: 100,
NoiseCount: 0,
ShowLineOptions: 0 | 2,
Length: 4,
Source: "123456879qwertyuiopasdfghlzxcvbnm",
BgColor: &color.RGBA{
R: 3,
G: 102,
B: 214,
A: 125,
},
Fonts: []string{"wqy-microhei.ttc"},
}
// 自定义配置,如果不需要自定义配置,则上面的结构体和下面这行代码不用写
var driverString base64Captcha.DriverString = captchaConfig
var driver base64Captcha.Driver = driverString.ConvertFonts()
// 这是自带的 store,存储数据的结构是变量
// var store = base64Captcha.DefaultMemStore
// 但是官方推荐 store 的存储数据的结构最好是redis,其有数据持久化的功能。
type RedisStore struct {
Conn redis.Conn
}
func (that *RedisStore) Set(id, value string) {
_, err := that.Conn.Do("set", id, value)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("redis set success")
}
}
func (that *RedisStore) Get(id string, clear bool) string {
val, err := redis.String(that.Conn.Do("get", id))
if err != nil {
fmt.Println(err)
} else {
fmt.Println("redis get success")
}
if clear {
that.Conn.Do("del", id)
}
return val
}
func (that *RedisStore) Verify(id, answer string, clear bool) bool {
val := that.Get(id, clear)
return val == answer
}
// 生成图形化验证码
func GenerateCaptcha(ctx *context.Context) (CaptchaResult, error) {
var conn = Pool.Get()
defer conn.Close()
var store = &RedisStore{conn}
// 驱动 + 仓库 =》验证码实例
var captcha = base64Captcha.NewCaptcha(driver, store)
// 验证码实例 =》产生一个 验证码
id, b64s, err := captcha.Generate()
if err != nil {
return CaptchaResult{}, err
}
return CaptchaResult{
Id: id,
Base64Blob: b64s,
}, nil
}
// 验证 前端 发过来的 验证码 是否正确
func CheckCaptcha(id, data string) bool {
var conn = Pool.Get()
defer conn.Close()
var store = &RedisStore{conn}
// 驱动 + 仓库 =》验证码实例
var captcha = base64Captcha.NewCaptcha(driver, store)
res := captcha.Verify(id, data, true)
return res
}
package tools
import (
"fmt"
"github.com/astaxie/beego/context"
)
type Status int
func (that Status) String() {
switch that {
case 0:
fmt.Println("该状态为 操作成功")
case 1:
fmt.Println("该状态为 操作失败")
}
}
//SUCCESS FAILED 枚举
const (
SUCCESS Status = 0 // 操作成功
FAILED Status = 1 //操作失败
)
//Success 成功 de 返回
func Success(ctx *context.Context, v interface{}) {
res := map[string]interface{}{
"code": SUCCESS,
"data": v,
}
ctx.Output.JSON(res, false, false)
}
//Failed 失败 de 返回
func Failed(ctx *context.Context, v interface{}) {
res := map[string]interface{}{
"code": FAILED,
"msg": v,
}
ctx.Output.JSON(res, false, false)
}
package tools
//Page is
type Page struct {
CurrentPage int `json:"currentPage"` //当前页
PageSize int `json:"pageSize"` // 一页个数
TotalPage int `json:"totalPage"` // 总的页数
TotalCount int `json:"totalCount"` //总的个数
FirstPage bool `json:"firstPage"` //是否首页
LastPage bool `json:"lastPage"` //是否尾页
Data interface{} `json:"data"` // 返回给前端的数据
}
//PageUtil count 总的数据个数; currentpage 当前页; pageSize 一页显示的个数
func PageUtil(count int, currentpage int, pageSize int, Data interface{}) Page {
tp := count / pageSize
if count%pageSize > 0 {
tp = count/pageSize + 1
}
return Page{
CurrentPage: currentpage,
PageSize: pageSize,
TotalPage: tp,
TotalCount: count,
FirstPage: currentpage == 1,
LastPage: currentpage == tp,
Data: Data,
}
}
Go 是一个独立的 HTTP 服务器,但是我们有些时候为了 nginx 可以帮我做很多工作,例如访问日志,cc 攻击,静态服务等,nginx 已经做的很成熟了,Go 只要专注于业务逻辑和功能就好,所以通过 nginx 配置代理就可以实现多应用同时部署,如下就是典型的两个应用共享 80 端口,通过不同的域名访问,反向代理到不同的应用。
server {
listen 80;
server_name .a.com;
charset utf-8;
access_log /home/a.com.access.log;
location /(css|js|fonts|img)/ {
access_log off;
expires 1d;
root "/path/to/app_a/static";
try_files $uri @backend;
}
location / {
try_files /_not_exists_ @backend;
}
location @backend {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8080;
}
}
server {
listen 80;
server_name .b.com;
charset utf-8;
access_log /home/b.com.access.log main;
location /(css|js|fonts|img)/ {
access_log off;
expires 1d;
root "/path/to/app_b/static";
try_files $uri @backend;
}
location / {
try_files /_not_exists_ @backend;
}
location @backend {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8081;
}
}
NameVirtualHost *:80
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.example.com
ServerName www.a.com
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.example.com
ServerName www.b.com
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPass / http://127.0.0.1:8081/
ProxyPassReverse / http://127.0.0.1:8081/
</VirtualHost>
通过github.com/gorilla/websocket可以在beego中使用websocket协议:
package main
import (
"fmt"
"net/http"
// "github.com/astaxie/beego" beego v1.x
beego "github.com/beego/beego/v2/server/web" // beego v2.x
"github.com/fatih/color"
"github.com/gorilla/websocket"
)
// 核心 协议升级结构体
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 允许跨域访问
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type Ws struct {
beego.Controller
}
func (that *Ws) Get() {
conn, err := upgrader.Upgrade(that.Ctx.ResponseWriter, that.Ctx.Request, nil)
if err != nil {
fmt.Println(err)
color.Red("升级成功")
} else {
color.Red("升级成功")
}
for {
fmt.Println("开始循环----接受---- 客户端发过来的信息")
msgType, msg, err := conn.ReadMessage()
if err != nil {
fmt.Println(err)
break
}
fmt.Println("接收到客户端发过来的信息是:", msg)
msg = append(msg, 'w', 't', 't')
fmt.Println("----发送----到客户端的信息是:", string(msg))
err = conn.WriteMessage(msgType, msg)
if err != nil {
fmt.Println(err)
break
}
}
}
func main() {
// websocket 测试地址: ws://localhost:8081/wtt
beego.Router("/wtt", &Ws{})
beego.Run("localhost:8081")
}
证书
(SSl证书)
,这个证书可以自己做,也可以到网站申请。appname = rxw
httpaddr = "0.0.0.0"
httpport = 8080
# 以下是https配置信息
EnableHTTPS = true
EnableHttpTLS = true
HTTPSPort = 8010
HTTPSCertFile = "server.crt" # 或者 HTTPSCertFile = "server.pem"
HTTPSKeyFile = "server.key"
autorender = false
copyrequestbody = true
runmode = prod
说明:
以上配置开启两个服务,监听两个端口,一个是http服务监听8080端口;另一个是https服务监听8010端口
# 键入以下命令:
openssl genrsa -des3 -out server.key 1024
# 反馈画面
Generating RSA private key, 1024 bit long modulus
.......................................++++++
..............................................++++++
e is 65537 (0x10001)
Enter pass phrase for server.key:
Verifying - Enter pass phrase for server.key:
设定一个密码(至少4位)并确认
# 键入以下命令:
openssl req -new -key server.key -out server.csr
# 反馈画面
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Shanghai
Locality Name (eg, city) []:Shanghai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:AAA
Organizational Unit Name (eg, section) []:OT
Common Name (e.g. server FQDN or YOUR name) []:127.0.0.1
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
# 键入以下两条命令:
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
openssl x509 -noout -text -in server.crt
├── Apache
│ ├── 1_root_bundle.crt
│ ├── 2_wtt.ruixiao.com.crt
│ └── 3_wtt.ruixiao.com.key
├── IIS
│ └── wtt.ruixiao.com.pfx
├── Nginx
│ ├── 1_wtt.ruixiao.com_bundle.crt (待使用)
│ └── 2_wtt.ruixiao.com.key (待使用)
├── Tomcat
│ └── wtt.ruixiaowan.com.jks
├── wtt.ruixiao.com.csr
├── wtt.ruixiao.com.key
└── wtt.ruixiao.com.pem