golang+mongodb构建一个博客的API服务
一、简介
这次作业的要求如下:
利用 web 客户端调用远端服务是服务开发本实验的重要内容。其中,要点建立 API First 的开发理念,实现前后端分离,使得团队协作变得更有效率。
因此,需要搭建一个后端服务器,并实现博客相关的API设计。这是一个小组项目,我负责的是博客中comment、like的全部功能。
二、配置相关
本次作业数据库用的是mongodb,后端用的语言是go,web框架用的是gin。gin是一个微框架,运行速度快、支持分组路由、支持错误处理、并提供中间件支持。在gin的安装过程中,由于有许多相应的依赖,最好先确认go的版本大于1.13,并通过如下命令开启代理,再进行安装。
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct
为了让go操作mongodb,还需要引入一个第三方包mgo。
服务器相关的安装在这篇博客中有对应的链接。
三、实现api中的功能
为了使博客能够进行点赞和评论,需要构建对应的API使得前端能够根据相应的API发起请求并获取数据。
构建路由如下:
注意到,使用了gin框架中的分组路由,这样可以很方便的将相关的操作划分成一组.
定义点赞和评论在数据库的存储格式:
type Comment struct {
Cid string `bson:"_id"`
Content string `bson:"content"`//评论内容
Publisher string `bson:"publisher"`//评论者用户名
Id string `bson:"id"` //评论的(文章或评论)的ID
}
type Like struct {
Lid string `bson:"_id"`
Liker string `bson:"liker"`//点赞者用户名
Id string `bson:"id"` //点赞的(文章或评论)的ID
}
接下来选取几个具体的例子进行讲解.首先是发表评论:
根据上面的评论的数据结构可知,一个评论在数据库存储时,需要包含一个Cid,评论内容,评论者的用户名,评论的文章或评论的ID(因为不仅可能需要给文章评论还可能需要给评论进行评论).由于Cid可以在插入数据库时生成,因此前端只需提供后三个参数即可.
后端先定义一个所需数据的结构体,并通过json解码即可获得前端传过来的参数.
type Comment_notPublished struct {
Content string `bson:"content"`//评论内容
Publisher string `bson:"publisher"`//评论者用户名
Id string `bson:"id"` //评论的(文章或评论)的ID
}
var comment Comment_notPublished
json.NewDecoder(c.Request.Body).Decode(&comment)
判断用户是否存在:
MyuserModel.DB.Find(bson.M{"username": comment.Publisher}).One(&tmpUser)
hexid := fmt.Sprintf("%x", string(tmpUser.Id))
if (hexid == "") {
c.JSON(http.StatusOK, &ApiResponse {
Code: 400,
Type: "fail",
Message: "publisher does not exist",
})
return
}
判断要评论的文章或评论是否存在,若存在则将评论内容插入评论的数据表中.
var articles []Article
var comments []Comment
MyarticleModel.DB.Find(bson.M{"_id": bson.ObjectIdHex(comment.Id)}).All(&articles)
MycommentModel.DB.Find(bson.M{"_id": bson.ObjectIdHex(comment.Id)}).All(&comments)
if len(articles) == 0 && len(comments) == 0 {
c.JSON(http.StatusOK, &ApiResponse {
Code: 400,
Type: "fail",
Message: "The contentID does not exist",
})
}else {
MycommentModel.DB.Insert(&comment)
c.JSON(http.StatusOK, &ApiResponse {
Code: 200,
Type: "success",
Message: "",
})
}
删除评论,由于可以对文章进行评论,也可以对文章的评论进行评论,也可以对文章的评论的评论进行评论…同时点赞可以对任意文章和评论进行点赞,因此一篇文章及其评论是一颗以文章为根节点的多叉树,因此在删除对应的文章或评论时,需要将其所有子节点全部删除.即需要先遍历该节点下的所有节点,遍历完后依次将其删除.首先根据前端传来的评论或文章的id查找所有的评论,再根据广度优先搜索一层一层的往下遍历,最后将所有子节点放入一个切片中,再执行删除操作.
相关代码:
func GetCommentsByid (id string) []string {
var comments []Comment
var commentsid []string
MycommentModel.DB.Find(bson.M{"id": id}).All(&comments)
for i,comment := range comments {
comments[i].Cid = fmt.Sprintf("%x", string(comment.Cid))
commentsid = append(commentsid, comments[i].Cid)
}
return commentsid
}
func GetAllSonComments (id string) []string{
var arrstr []string
var res []string
arrstr = append(arrstr, id)
i := 0
res = append(res, id)
for ; i < len(arrstr); i++ {
res = append(res, GetCommentsByid(arrstr[i])...)
arrstr = append(arrstr, GetCommentsByid(arrstr[i])...)
arrstr = arrstr[1:] // 删除开头1个元素
i = -1
}
return res
}
func DeleteCommentByCid (c *gin.Context) {
var delecomments []string
delecomments = append(delecomments,GetAllSonComments(c.Param("id"))...)
for _,v := range delecomments {
MycommentModel.DB.Remove(bson.M{"_id": bson.ObjectIdHex(v)})
MylikeModel.DB.Remove(bson.M{"id": v})
}
c.JSON(http.StatusOK, &ApiResponse {
Code: 200,
Type: "success",
Message: "",
})
}
总结
这次作业担任了后端方面的工作,学会了用go的gin框架开启服务,配合mongodb可以为前端提供所需的API服务.