Beego学习笔记

LeslieLin 编程技术
阅读量 0 评论量 0

Beego介绍

beego是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API、Web 及后端服务等各种应用,是一个 RESTful 的框架,主要设计灵感来源于 tornado、sinatra 和 flask 这三个框架,但是结合了 Go 本身的一些特性(interface、struct 嵌入等)而设计的一个框架。

目前国内用的比较多的就是beegogin两个框架,如果项目比较小,个人开发,并且只是用golang来写一些api接口的话,gin是不错的选择,如果是团队开发或者不仅要用golangapi,还要用golangweb后端,并且注重代码质量的话建议使用beego

Feature特性

  1. MVC
  2. REST
  3. 智能路由
  4. 日志调试
  5. 配置管理
  6. 模板自动渲染
  7. layout设计
  8. 中间件插入逻辑
  9. 方便的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也就到这里,之后会编写项目来加深熟练度和规范代码。

喵~