Beego介绍
beego
是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API、Web 及后端服务等各种应用,是一个 RESTful 的框架,主要设计灵感来源于 tornado、sinatra 和 flask 这三个框架,但是结合了 Go 本身的一些特性(interface、struct 嵌入等)而设计的一个框架。
目前国内用的比较多的就是beego
和gin
两个框架,如果项目比较小,个人开发,并且只是用golang
来写一些api
接口的话,gin
是不错的选择,如果是团队开发或者不仅要用golang
写api
,还要用golang
写web
后端,并且注重代码质量的话建议使用beego
。
Feature特性
- MVC
- REST
- 智能路由
- 日志调试
- 配置管理
- 模板自动渲染
- layout设计
- 中间件插入逻辑
- 方便的JSON/XML服务
Beego脚手架安装
bee脚手架
go get github.com/beego/bee # 全局安装
Set设置环境变量Mac/Linux
#go语言安装主根目录
export GOROOT=/usr/local/go #替换你的目录
#GOPATH 是自己的go项目路径,自定义设置
export GOPATH=/Users/ding/go_workspace #替换你的目录
#GOBIN 当我们使用go install命令编译后并且安装的二进制程序目录
export GOBIN=$GOPATH/bin
# 启用 Go Modules 功能
export GO111MODULE=on
# 配置 GOPROXY 环境变量
export GOPROXY=https://goproxy.cn,direct
export PATH=$PATH:$GOROOT/bin:$GOBIN
Create创建第一个项目
bee new beegodemo
Init初始化模块依赖 & 运行
进入到项目根目录执行如下
go mod init beegodemo
bee run
Router路由注册
在项目路径routers/router.go
文件
package routers
import (
"beegodemo/controllers"
"github.com/astaxie/beego"
)
func init() {
/*
分析beego.Router方法
func Router(rootpath string, c ControllerInterface, mappingMethods ...string)
可以看到方法需传入三个参数,第一个为访问路径,第二个为控制器的指针地址,第三个参数具体解析到指定的方法(可选参数)
*/
beego.Router("/", &controllers.MainController{})
beego.Router("/reg", &controllers.OrmTestController{}) // 如果不传入第三个参数,将会默认指向控制器的Get()方法
beego.Router("/login", &controllers.OrmTestController{}, "get:Login") // 支持get/post/put/delete等方式,其中Login为OrmTest控制器下的Login方法
}
OrmTestController.go
package controllers
import (
"beegodemo/models"
"fmt"
"github.com/astaxie/beego/orm"
)
// 定义结构体,继承beego的MainController控制器
type OrmTestController struct {
MainController
}
func (this OrmTestController) Get() {
ormCase := orm.NewOrm()
pass := models.PassEncode(this.GetString("password"))
user := models.User{}
user.Username = this.GetString("username") // 获取GET/POST参数
user.Password = pass
id, err := ormCase.Insert(&user)
if err != nil {
this.Ctx.WriteString(err.Error())
return
}
fmt.Println(id)
this.Ctx.WriteString("插入成功")
}
func (this OrmTestController) Login() {
ormCase := orm.NewOrm()
pass := this.GetString("password")
user := models.User{
Username: "3",
}
// user.Username = this.GetString("username")
errS := ormCase.Read(&user, "username")
if errS != nil {
fmt.Println(errS.Error())
return
}
err := models.ComparePass(user.Password, pass)
if err != nil {
this.Ctx.WriteString("用户密码不正确")
return
}
this.Ctx.WriteString("登录成功")
}
Model模型
Web 应用中我们用的最多的就是数据库操作,而 model 层一般用来做这些操作。说的简单一点,如果应用足够简单,那么Controller
可以处理一切的逻辑,如果逻辑里面存在着可以复用的东西,那么就抽取出来变成一个模块。因此Model
就是逐步抽象的过程,一般会在Model
里面处理一些数据读取,如下是我自定义一些工具类的代码片段:
package models
import (
"fmt"
"github.com/astaxie/beego"
"golang.org/x/crypto/bcrypt"
"time"
)
// @Description 时间戳转日期
func UnixToDate(timestamp int) string {
t := time.Unix(int64(timestamp), 0)
return t.Format("2006-01-02 15:01:05")
}
func DateToUnix(str string) int64 {
template := "2006-01-02 15:01:05"
t, err := time.ParseInLocation(template, str, time.Local)
if err != nil {
beego.Info(err)
}
return t.Unix()
}
// @Description 获取当前日期
func GetDate() string {
t := time.Now()
return t.Format("2006-01-02 15:01:05")
}
// @Description 密码HASH加密
func PassEncode(str string) string {
hash, err := bcrypt.GenerateFromPassword([]byte(str), bcrypt.DefaultCost) //加密处理
if err != nil {
fmt.Println(err)
}
encodePWD := string(hash)
return encodePWD
}
// @Description 验证密码
func ComparePass(oldPass string, inputPass string) error {
return bcrypt.CompareHashAndPassword([]byte(oldPass), []byte(inputPass))
}
在beego
应用注册我的工具
beego.AddFuncMap("unixToDate", models.UnixToDate)
beego.AddFuncMap("DateToUnix", models.DateToUnix)
beego.AddFuncMap("Md5Encode", models.PassEncode)
beego.AddFuncMap("ComparePass", models.ComparePass)
ORM引入
beego
官方提供了自己的orm库,用就完事。
go get github.com/astaxie/beego/orm
package main
// main.go
import (
"beegodemo/models"
_ "beegodemo/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
// 注入ORM
func init() {
// 参数1 driverName
// 参数2 数据库类型
// 这个用来设置 driverName 对应的数据库类型
// mysql / sqlite3 / postgres / tidb 这几种是默认已经注册过的,所以可以无需设置
orm.RegisterDriver("mysql", orm.DRMySQL)
// 参数1 数据库的别名,用来在 ORM 中切换数据库使用
// 参数2 driverName
// 参数3 对应的链接字符串
orm.RegisterDataBase("default", "mysql", "beego:beegogo@/beego?charset=utf8") // 用户名:密码@/数据库名?charset=utf8
}
func main() {
var FilterToken = func(ctx *context.Context) {
beego.Info(ctx.Input.Header("Authorization"))
}
beego.InsertFilter("/*", beego.BeforeRouter, FilterToken)
beego.AddFuncMap("unixToDate", models.UnixToDate)
beego.AddFuncMap("DateToUnix", models.DateToUnix)
beego.AddFuncMap("Md5Encode", models.PassEncode)
beego.AddFuncMap("ComparePass", models.ComparePass)
beego.Run()
}
Use使用
在项目目录models
下新建一个models.go
文件即注册一个模型,代码片段如下
package models
import (
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
"time"
)
// 定义User结构体, 第一个字段会被作为自增pkey
type User struct {
Id int
Username string
Password string
Created_at time.Time `orm:"auto_now_add;type(datetime)"`
Updated_at time.Time `orm:"auto_now;type(datetime)"`
}
func init() {
orm.RegisterDataBase("default", "mysql", "beego:beegogo@/beego?charset=utf8")
orm.RegisterModel(new(User)) // 实例化User结构体 RegisterModel 也可以同时注册多个model
orm.RunSyncdb("default", false, true) // 自动迁移
}
Simple简单的增删查改
ormCase := orm.NewOrm()
pass := models.PassEncode(this.GetString("password"))
user := models.User{}
user.Username = this.GetString("username")
user.Password = pass
// 增
ormCase.Insert(&user)
// 改
user.Password = "change password"
ormCase.Update(&user)
// 查
find := new(User)
find.username = "find"
ormCase.Read(find)
// 删
delete := new(User)
delete.Id = 1
ormCase.Delete(delete)
Add添加JWT认证以及过滤器
首先引入jwt
go get github.com/dgrijalva/jwt-go
框架中引入jwt
import (
"fmt"
"github.com/astaxie/beego"
"github.com/dgrijalva/jwt-go"
)
Code编写jwt结构体以及相应方法
package models
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"regexp"
"time"
)
const (
KEY string = "JWT-ARY-STARK" // 秘钥
DEFAULT_EXPIRE_SECONDS int = 600 // 默认过期时间(s)
)
// JWT -- json web token
// HEADER PAYLOAD SIGNATURE
// This struct is the PAYLOAD
type MyCustomClaims struct {
User
jwt.StandardClaims
}
//刷新jwt token
func RefreshToken(tokenString string) (string, error) {
// first get previous token
token, err := jwt.ParseWithClaims(
tokenString,
&MyCustomClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte(KEY), nil
})
claims, ok := token.Claims.(*MyCustomClaims)
if !ok || !token.Valid {
return "", err
}
mySigningKey := []byte(KEY)
expireAt := time.Now().Add(time.Second * time.Duration(DEFAULT_EXPIRE_SECONDS)).Unix()
newClaims := MyCustomClaims{
claims.User,
jwt.StandardClaims{
ExpiresAt: expireAt,
Issuer: claims.User.Username,
IssuedAt: time.Now().Unix(),
},
}
// generate new token with new claims
newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
tokenStr, err := newToken.SignedString(mySigningKey)
if err != nil {
fmt.Println("generate new fresh json web token failed !! error :", err)
return "", err
}
return tokenStr, err
}
//验证jtw token
func ValidateToken(tokenString string) (info User, err error) {
token, err := jwt.ParseWithClaims(
tokenString,
&MyCustomClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte(KEY), nil
})
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
//fmt.Printf("%v %v", claims.User, claims.StandardClaims.ExpiresAt)
//fmt.Println("token will be expired at ", time.Unix(claims.StandardClaims.ExpiresAt, 0))
info = claims.User
} else {
fmt.Println("validate tokenString failed !!!", err)
}
return
}
//获取jwt token
func GenerateToken(info *User, expiredSeconds int) (tokenString string, err error) {
if expiredSeconds == 0 {
expiredSeconds = DEFAULT_EXPIRE_SECONDS
}
// Create the Claims
mySigningKey := []byte(KEY)
expireAt := time.Now().Add(time.Second * time.Duration(expiredSeconds)).Unix()
fmt.Println("token will be expired at ", time.Unix(expireAt, 0))
// pass parameter to this func or not
user := *info
claims := MyCustomClaims{
user,
jwt.StandardClaims{
ExpiresAt: expireAt,
Issuer: user.Username,
IssuedAt: time.Now().Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenStr, err := token.SignedString(mySigningKey)
if err != nil {
fmt.Println("generate json web token failed !! error :", err)
} else {
tokenString = tokenStr
}
return
}
// return this result to client then all later request should have header "Authorization: Bearer <token> "
func GetHeaderTokenValue(tokenString string) string {
//Authorization: Bearer <token>
tokenRegExp := regexp.MustCompile(`Bearer (.*)`)
finalToken := tokenRegExp.FindStringSubmatch(tokenString)
return finalToken[1]
}
Use使用
package controllers
import (
"beegodemo/models"
"fmt"
)
type UserController struct {
MainController
}
// 获取token
func (this *UserController) Get() {
user := models.User{Id: 1, Username: "fuzzy"}
token, err := models.GenerateToken(&user, 0)
if err != nil {
fmt.Println(err)
} else {
// 获取jwt
this.Ctx.WriteString(token)
}
}
// 验证token
func (this *UserController) Check() {
token := "token"
info, err := models.ValidateToken(token)
if err != nil {
this.Ctx.WriteString(err.Error())
this.StopRun()
}
fmt.Println(info)
this.Ctx.WriteString("success")
}
Register注册过滤器
func main() {
FilterToken := func(ctx *context.Context) {
beego.Info(ctx.Input.Header("Authorization"))
// 过滤不需要验证的地址
if ctx.Request.RequestURI != "/login" && ctx.Input.Header("Authorization") == "" {
logs.Error("without token, unauthorized !!")
ctx.ResponseWriter.WriteHeader(401)
ctx.ResponseWriter.Write([]byte("no permission"))
}
if ctx.Request.RequestURI != "/login" && ctx.Input.Header("Authorization") != "" {
token := models.GetHeaderTokenValue(ctx.Input.Header("Authorization"))
logs.Info("curernttoken: ", token)
info, err := models.ValidateToken(token) // 验证token
if err != nil {
ctx.WriteString(err.Error())
ctx.Redirect(401, "/")
}
logs.Info(info)
}
}
beego.InsertFilter("/*", beego.BeforeRouter, FilterToken) // 注册过滤器,beego.BeforeRouter会在寻找路由之前进行过滤处理
beego.AddFuncMap("unixToDate", models.UnixToDate)
beego.AddFuncMap("DateToUnix", models.DateToUnix)
beego.AddFuncMap("Md5Encode", models.PassEncode)
beego.AddFuncMap("ComparePass", models.ComparePass)
beego.AddFuncMap("GenerateToken", models.GenerateToken)
beego.AddFuncMap("ValidateToken", models.ValidateToken)
beego.AddFuncMap("GetHeaderTokenValue", models.GetHeaderTokenValue)
beego.Run()
}
至此,jwt
验证流程结束。
手撕beego
也就到这里,之后会编写项目来加深熟练度和规范代码。