![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4fb2b42cad6aaffcf1337d8f7fd751a2.png#pic_center)
一、建立路由
这里要稍微注意的是:需要登录后才可以发表帖子,所以需要用到我们之前写的鉴权中间件。中间件对用户携带的token
解析成功后,便会将用户信息存入ctx
中,我们如果从ctx
中能取出用户信息,说明鉴权成功,否则失败。
router/route.go
// 需要登录后才可以发表帖子,所以需要鉴权中间件
v1.POST("/post", middlewares.JWTAuthMiddleware(), controller.CreatePostHandler)
为了文档看起来更清晰,我这里将中间件的代码和从ctx
中获取用户id
的方法再贴一下。
middlewares/auth.go
package middlewares
import (
"bluebell/constdef"
"bluebell/controller"
"bluebell/pkg/jwt"
"strings"
"github.com/gin-gonic/gin"
)
// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
// 这里假设Token放在Header的Authorization中,并使用Bearer开头
// Authorization: Bearer xxxxxxx.xxx.xxx / X-TOKEN: xxx.xxx.xx
// 这里的具体实现方式要依据你的实际业务情况决定,和前端对其即可
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
controller.ResponseError(c, controller.CodeNeedLogin)
c.Abort()
return
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
controller.ResponseError(c, controller.CodeInvalidToken)
c.Abort()
return
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
mc, err := jwt.ParseToken(parts[1])
if err != nil {
controller.ResponseError(c, controller.CodeInvalidToken)
c.Abort()
return
}
// 将当前请求的userId信息保存到请求的上下文c上
c.Set(constdef.CtxUserIDKey, mc.UserID)
c.Next() // 后续的处理请求的函数中 可以用过c.Get(CtxUserIDKey) 来获取当前请求的用户信息
}
}
controller/request.go
package controller
import (
"bluebell/constdef"
"errors"
"strconv"
"github.com/gin-gonic/gin"
)
var ErrorUserNotLogin = errors.New("用户未登录")
// getCurrentUserID 获取当前登录的用户ID
func getCurrentUserID(c *gin.Context) (userID int64, err error) {
uid, ok := c.Get(constdef.CtxUserIDKey)
if !ok {
err = ErrorUserNotLogin
return
}
userID, ok = uid.(int64)
if !ok {
err = ErrorUserNotLogin
return
}
return
}
二、开发CreatePostHandler
controller/post.go
// CreatePostHandler 创建帖子的处理函数
func CreatePostHandler(c *gin.Context) {
// 1. 获取参数及参数的校验
//c.ShouldBindJSON() // validator --> binding tag
p := new(models.Post)
if err := c.ShouldBindJSON(p); err != nil {
zap.L().Debug("c.ShouldBindJSON(p) error", zap.Any("err", err))
zap.L().Error("create post with invalid param")
ResponseError(c, CodeInvalidParam)
return
}
// 从 c 取到当前发请求的用户的ID
userID, err := getCurrentUserID(c)
if err != nil {
ResponseError(c, CodeNeedLogin)
return
}
p.AuthorId = userID
// 2. 创建帖子
if err := logic.CreatePost(p); err != nil {
zap.L().Error("logic.CreatePost(p) failed", zap.Error(err))
ResponseError(c, CodeServerBusy)
return
}
// 3. 返回响应
ResponseSuccess(c, nil)
}
三、编写logic
logic/post.go
package logic
import (
"bluebell/dao/mysql"
"bluebell/models"
"go.uber.org/zap"
)
func CreatePost(post *models.Post) error {
// 1. 使用雪花算法生成post id
post.PostId = snowflake.GenID()
// 2. 保存到数据库中
err := mysql.CreatePost(post)
if err != nil {
return err
}
return nil
}
四、编写dao层
mysql/post.go:增加获取帖子详情函数
func CreatePost(post *models.Post) error {
return db.Create(&post).Error
}
五、编译测试运行
可以看到是创建成功的