上一节完成了用户登录。本节我们来编写发布文章。
首先在models目录中创建一下文件article.go,用它来编写article模型。
package models
import (
"gorm.io/gorm"
)
type Article struct {
gorm.Model
Title string `gorm:"type:varchar(255);not null" json:"title"`
Content string `gorm:"type:text;not null" json:"content"`
UserID uint `gorm:"not null" json:"user_id"`
User User `gorm:"foreignKey:UserID"`
}
这定义了一个名为Article的结构体,它将映射到数据库中的一个表,用来存储文章数据。结构体中的每个字段表示表中的一个列。前3个字段就不多介绍了。
User字段定义了与User结构体的关联,这里使用了GORM的foreignKey标签来指定外键:
- gorm:"foreignKey:UserID": 表示User结构体通过UserID字段与Article结构体相关联。
接下修改models/db.go文件中的:err = db.AutoMigrate(&User{}, &Article{})增加&Article{}来确保程序运行时能创建Article模型对应的数据库表。
模型相关的代码编写完成后,我们把接口需要代码实现,在api目录中新建article.go文件:
package api
import (
"net/http"
"xblog/models"
"github.com/gin-gonic/gin"
)
// CreateArticle 创建新文章
func CreateArticle(c *gin.Context) {
// 从上下文中获取当前用户
currentUser, _ := c.Get("currentUser")
user, ok := currentUser.(*models.JwtClaims)
if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户认证信息"})
return
}
// 初始化Article结构体
var article models.Article
// 绑定请求体到article结构体
if err := c.ShouldBindJSON(&article); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 设置文章的UserID为当前用户的ID
article.UserID = user.UserID
// 保存文章到数据库
if result := models.DB.Create(&article); result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
// 预加载User数据
if result := models.DB.Preload("User").First(&article, article.ID); result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "加载文章详情时出现错误"})
return
}
// 返回创建的文章
c.JSON(http.StatusOK, gin.H{"article": article})
}
我从CreateArticle函数开始解释代码:
- CreateArticle函数定义了如何处理创建文章的HTTP请求,它接收一个类型为*gin.Context的参数,这是Gin框架中用于处理HTTP请求和响应的上下文对象。
- 通过c.Get("currentUser")从上下文中获取当前用户的信息。currentUser可以是在之前的请求处理中设置的,用于表示已经认证的用户。
- 通过断言检查currentUser是否可以转换为*models.JwtClaims类型,如果不成功,则返回HTTP 401 Unauthorized状态码,并携带错误信息。你还记得currentUser这个变量是怎么来的吗?是的上一节中,验证通过后,我将用户信息设置到Context中,供后续处理函数使用。
- 初始化article为models.Article类型的变量,准备绑定请求体中的数据。
- 使用c.ShouldBindJSON(&article)将请求体的JSON数据绑定到article变量,如果有错误发生,则返回HTTP 400 Bad Request状态码,并携带错误信息。
- 将当前用户的ID设置为文章的UserID字段,确保文章与创建它的用户关联。
- 使用models.DB.Create(&article)将article保存到数据库中,如果保存过程中出现错误,则返回HTTP 500 Internal Server Error状态码,并携带错误信息。
- 使用models.DB.Preload("User").First(&article, article.ID)预加载与文章关联的用户数据,并根据文章ID查询文章。如果查询过程中发生错误,则返回HTTP 500 Internal Server Error状态码,并携带错误信息。
- 最后,如果文章成功创建且用户数据预加载成功,函数以HTTP 200 OK状态码返回创建的文章数据。
简单来说这个函数属于一个典型的CRUD(创建、读取、更新、删除)操作中的“创建”操作,它使用Gin框架和GORM库来处理HTTP请求并与数据库交互。
最后我们把路由写一下。
package router
import (
"xblog/api"
"xblog/utils"
"github.com/gin-gonic/gin"
)
func InitRouter() {
// 创建默认路由实例
r := gin.Default()
// 配置路由
public := r.Group("api/v1")
{
public.POST("user/add", api.UserAdd)
public.POST("user/login", api.UserLogin)
}
// 配置需要认证的路由
protected := r.Group("api/v1")
protected.Use(utils.RequireAuth)
{
protected.POST("article/create", api.CreateArticle)
}
r.Run(":8000")
}
以上是router.go文件中的全部内容,只有22-26这几行是新增的。我们来逐一解释一下:
- protected := r.Group("api/v1") - 创建一个名为protected的路由组,同样以api/v1作为前缀。
- protected.Use(utils.RequireAuth) - 给protected路由组添加一个中间件utils.RequireAuth,这个中间件是用来确保请求已经通过了认证。任何属于这个组的路由都需要用户被认证后才可访问。
- 在protected路由组中,配置了一个POST请求的路由: protected.POST("article/create", api.CreateArticle): 当请求POST /api/v1/article/create时,将调用api.CreateArticle函数来处理。
这个InitRouter函数是设置Gin路由的标准方式,它通过路由组和中间件提供了灵活的路由管理功能。通过区分public和protected路由组,可以很容易地管理哪些API端点是公开的,哪些需要用户认证。
到这里发布文章的代码就都完成了。让我们来运行程序,使用curl测试一下:
从上图可以看出,先使用curl模拟用户登录,并获取token。然后再次使用curl并且需要携带token才能发布文章。
好,没有问题。让我们登录MySQL查看一下数据。
很好,数据成功写入了数据库。
本文暂时没有评论,来添加一个吧(●'◡'●)