专业的JAVA编程教程与资源

网站首页 > java教程 正文

聚合接口层(BFF)之Go语言版(三)统一响应、中间件

temp10 2024-10-23 15:07:45 java教程 11 ℃ 0 评论

书接上文:

聚合接口层(BFF)之Go语言版(一)

聚合接口层(BFF)之Go语言版(三)统一响应、中间件

聚合接口层(BFF)之Go语言版(二)获取请求Header、参数

统一响应

返回JSON格式的响应,前文用的都是:

ctx.JSON(http.StatusOK, gin.H{"userName": userName})

实际工作中一般响应格式:

{
    "code": 0,
    "msg": "success",
    "data": {
        "id": "111AAA",
        "userName": "张三"
    }
}

每个请求都手工拼接JSON响应,不现实,需要一个统一的响应。

common包下新建文件ResponseVo.go

package common

import (
  "net/http"

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

type ResponseVo struct {
  Code     int32       `json:"code"`
  Msg      string      `json:"msg"`
  DataName string      `json:"dataName"`
  Data     interface{} `json:"data,omitempty"`
}

func SuccessResponse(ctx *gin.Context, dataName string, data interface{}) {
  ctx.JSON(http.StatusOK, ResponseVo{Code: 0, Msg: "success", DataName: dataName, Data: data})
}

func FailResponse(ctx *gin.Context, msg string, dataName string, data interface{}) {
  ctx.JSON(http.StatusOK, ResponseVo{Code: 1, Msg: msg, DataName: dataName, Data: data})
}

func SuccessVo(dataName string, data interface{}) ResponseVo {
  return ResponseVo{Code: 0, Msg: "success", DataName: dataName, Data: data}
}

func FailVo(msg string, dataName string, data interface{}) ResponseVo {
  return ResponseVo{Code: 1, Msg: msg, DataName: dataName, Data: data}
}

SuccessResponse、FailResponse可以直接将响应输出至前端。

SuccessVo、FailVo会返回响应至Controller,供进一步处理。

具体的参数,可以根据实际工作需要进行增减。例如:

func FailResponseWithoutData(ctx *gin.Context, msg string, dataName string) {
  ctx.JSON(http.StatusOK, ResponseVo{Code: 1, Msg: msg, DataName: dataName, Data: nil})
}

func FailResponseWithCode(ctx *gin.Context, code int32, msg string, dataName string) {
  ctx.JSON(http.StatusOK, ResponseVo{Code: code, Msg: msg, DataName: dataName, Data: nil})
}

dataName的作用后续会介绍,先放在这。

code是业务码,与HTTP的响应码要区分开。

msg是具体的业务错误信息,例如:XX参数校验失败。Token失效等等。

data是具体的响应内容,在Java里这是一个T(泛型),在Go里就是interface{}。

Code     int32       `json:"code"`

代表JSON序列化反序列化的时候,key是“code”。

`json:"data,omitempty"`

omittempty代表此字段可以为nil。

至此,Controller可以改造如下:

func GetParamFronPost(ctx *gin.Context) {
  userAddParam := common.UserAddParam{}

  bindErr := ctx.ShouldBindJSON(&userAddParam)
  if bindErr != nil {
    common.FailResponse(ctx, "参数转换失败", "UserAddParam", nil)
    return
  }

  // 如果是13位时间戳,需/1000
  unixTime := time.Unix(userAddParam.CreateTime, 0)
  formattedTime := unixTime.Format(time.DateTime)
  log.Println(userAddParam.UserName, userAddParam.Account.Mobile, userAddParam.CreateTime, formattedTime)
  // 方案一
  data := common.SuccessVo("UserAddParam", userAddParam)
  ctx.JSON(http.StatusOK, data)
  // 方案二(推荐)
  //common.SuccessResponse(ctx, "UserAddParam", userAddParam)
}

中间件

Go的中间件,大家可以当成是Java里的Filter。

例如Javaer经常用Filter校验Token,在Go里面可以这么写。

common包新建CurrentUserInfo.go

package common

var CURRENT_USER_INFO = "CurrentUserInfo"

type CurrentUserInfo struct {
  UserId   string `json:"userId"`
  UserName string `json:"userName"`
}

新增文件夹middleware,新增tokenCheck.go:

package middleware

import (
  "gows/common"
  "log"

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

func TokenCheck() gin.HandlerFunc {
  return func(ctx *gin.Context) {
    token := ctx.Request.Header.Get("Token")
    if len(token) != 0 {
      log.Println("Token内容:", token)
      //此处应该解析JWT Token,并从Redis或DB处获取用户信息
      //如果解析失败,或令牌过期、用户被锁,则直接返回错误信息
      currentUserInfo := common.CurrentUserInfo{
        UserId:   token,
        UserName: "用户名:" + token,
      }
      //将用户信息写入Context,供Controller使用
      ctx.Set(common.CURRENT_USER_INFO, currentUserInfo)
      //执行下一个中间件
      ctx.Next()
    } else {
      log.Println("没有Token")
      //不使用HTTP响应码
      //common.FailResponse(ctx, "没有Token", "token", nil)
      //使用HTTP响应码s
      ctx.AbortWithStatusJSON(403, common.FailVo("没有Token", "", nil))
    }
  }
}

写一个测试Controller方法:

func MiddleWareTest(ctx *gin.Context) {
  currentUserInfo, exists := ctx.Get(common.CURRENT_USER_INFO)
  if !exists {
    common.FailResponse(ctx, "有Token,没用户", "token", nil)
    return
  }
  loginUser := currentUserInfo.(common.CurrentUserInfo)
  common.SuccessResponse(ctx, "loginUser", loginUser)
}

接着改造main.go里面的路由配置:

// 新增一个名为authGroup的路由组,拦截路径以/api开头的请求,使用TokenCheck中间件
authGroup := r.Group("/api", middleware.TokenCheck())
//在authGroup路由组下新增请求/testmw
authGroup.GET("/testmw", controller.MiddleWareTest)

请求如下:

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表