Go语言 基于gin框架从0开始构建一个bbs server(三)-中间件,makefile与air

510 阅读3分钟

jwt 中间件

前面介绍了jwt的基本使用,但是那种写法比较不方便 你要是每个路由 都要增加这个特性,要复制粘贴很多代码,这里我们可以使用中间件来优化

func JWTAuthMiddleWare() func(context *gin.Context) {
   return func(context *gin.Context) {
      token := context.Request.Header.Get("auth-token")
      // 注意abort的写法 只要发现 token不合法 则直接abort 不会传到真正的路由那里
      if token == "" {
         controllers.ResponseError(context, controllers.CodeTokenIsEmpty)
         context.Abort()
         return
      }
      parseToken, err := jwt.ParseToken(token)
      if err != nil {
         controllers.ResponseError(context, controllers.CodeTokenInvalid)
         context.Abort()
         return
      }
      
      // 当token合法的时候 我们可以把对应的关键信息 取出来放到context里面,则对应的路由就可以直接通过get的方法 取出关键信息了
      zap.L().Debug("token parse", zap.String("username", parseToken.UserName))
      context.Set("username", parseToken.UserName)
      context.Set("userId", parseToken.UserId)
      context.Next()
   }
}

这样可以直接用 ,会方便不少

//验证jwt机制
r.GET("/ping", JWTAuthMiddleWare(), func(context *gin.Context) {
   // 这里post man 模拟的 将token auth-token
   zap.L().Debug("ping", zap.String("ping-username", context.GetString("username")))
   controllers.ResponseSuccess(context, "pong")
})

也可以增加一个方法 来统一的取值

package controllers

import (
   "errors"
   "go_web_app/middleware"

   "github.com/gin-gonic/gin"
)

var (
   ErrorNotLogin = errors.New("no login")
)

func getCurrentUserId(c *gin.Context) (int64, error) {
   userId, ok := c.Get(middleware.ContextUserIdKey)
   if !ok {
      return 0, ErrorNotLogin
   }
   return userId.(int64), nil
}

func getCurrentUserName(c *gin.Context) (string, error) {
   userName, ok := c.Get(middleware.ContextUserIdKey)
   if !ok {
      return "", ErrorNotLogin
   }
   return userName.(string), nil
}

代码中的循环引用问题

上述的代码在编译的时候 会有一些问题

image.png

middleware这个里面有引用controller的东西 image.png

controller下面 又有引用middleware下面的东西 image.png

这里就是循环引用了 你调用我 我调用你。

那要修改起来其实也简单 只要挪个位置就行

把我们的key 挪到controller包下 即可

package controllers

import (
   "errors"

   "github.com/gin-gonic/gin"
)

const ContextUserNameKey = "username"
const ContextUserIdKey = "userid"

var (
   ErrorNotLogin = errors.New("no login")
)

func getCurrentUserId(c *gin.Context) (int64, error) {
   userId, ok := c.Get(ContextUserIdKey)
   if !ok {
      return 0, ErrorNotLogin
   }
   return userId.(int64), nil
}

func getCurrentUserName(c *gin.Context) (string, error) {
   userName, ok := c.Get(ContextUserIdKey)
   if !ok {
      return "", ErrorNotLogin
   }
   return userName.(string), nil
}

makefile

我自己是在mac上开发的,然而你要发布到 linux上 你得编译成linux下的可执行文件,每次敲命令是有点麻烦的。 用makefile 可以大大降低这个成本

# 告诉makefile 不需要去当前目录找这些同名的文件 只要makefile 文件中 找即可
.PHONY: all build run gotool clean help

# 定义一个变量 项目的名称
BINARY="bbs_server"

# 定义2个目标
all: gotool build

# 我自己的开发机器是mac 所以最终要在linux上部署 需要build出来 linux下的可执行文件
build:
   CGO_ENABled=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY}
# 不加@ 则在make的时候 会打印出来对应的命令
run:
   @go run ./main.go config.yaml

# 格式化代码 并检查
gotool:
   go fmt ./
   go vet ./

# 删除对应的文件
clean:
   @if [ -f ${BINARY} ]; then rm ${BINARY} ; fi

help:
   @echo "make - 格式化 Go代码 并编译成二进制文件"
   @echo "make build - 编译 Go代码 并编译成linux平台下的二进制文件"
   @echo "make run - 直接运行"
   @echo "make clean 移除二进制文件"
   @echo "make gotool 运行go工具 fmt 和vet "

我们这里比较简单,主要是为了演示用。自己的项目可能比这个要复杂的多,makefile 作用就越大

air

通常而言 我们在开发的时候 希望我们保存代码以后 程序能够自动编译并且运行,这在前端领域或者是python领域很常见,在go中也有类似的方案

github.com/cosmtrek/ai…

在项目的根目录中 新建一个.air.conf 的文件 image.png

# 表示当前路径
root="."
# 临时文件目录
tmp_dir="tmp"

[build]
# 编译的命令
cmd="go build -o ./tmp/main"
# 最终可执行文件的名字
bin="/tmp/main"
# 执行程序的命令
full_bin="./tmp/main ./config.yaml"
# 监听哪些扩展名的文件
include_ext=["go","yaml","tpl","html"]
# 忽略文件扩展名或目录
exclude_dir=["assets","tmp"]
# 编译延迟 防止构建频繁
delay=1000
# 构建错误时 停止运行旧的二进制文件
stop_on_error=true
# air 的日志名
log="air_errors.log"
# 显示日志时间
time=true
[color]
main="magenta"
watcher="cyan"
build="yellow"
runner="green"

[misc]
# 退出时删除tmp目录
clean_on_exit=true

在项目的根目录中 输入 air 命令

image.png

至此,我们就可以完成 修改任意文件以后 程序自动build的 功能了,这可以极大的提高我们的开发效率