技能学习:学习使用golang(gin框架) + vue.js,开发前端全栈网站-7.GO语言做通用CRUD接口-2

1.制作分类功能的列表页

CategoryList.vue:

<template>
<div>
<h1>分类列表</h1>
<el-table :data="items">
<el-table-column prop="_id" label="ID" width="220">
</el-table-column>
<el-table-column prop="parent.name" label="上级分类">
</el-table-column>
<el-table-column prop="name" label="分类名称">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button type="text" size="small" @click="$router.push('/categories/edit/' + scope.row._id)">编辑</el-button>
<el-button @click="remove(scope.row)" type="text" size="small">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
items: []
}
},
methods: {
async fetch(){
const res = await this.$http.get('rest/categories')
this.items = res.data
},
remove(row){
this.$confirm('是否确定要删除分类"' + row.name + '"?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
// 要想使用await,函数必须使用async
// await异步执行,待调用接口获取数据完成后再将值传给res,进行下一步操作
const res = await this.$http.delete('rest/categories/' + row._id)
this.$message({
type: 'success',
message: '删除成功!'
});
if(res.status == 200){
// 接口调用成功后,刷新页面
this.fetch()
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
},
created() {
this.fetch()
}
}
</script>

解释:

在首页导航中引入该页面:

在路由页面引入组件和路由:

保存进入admin端页面查看效果:

2.CRUD接口中的查找数据

可以看到上方页面已经显示成功,但是里边的数据并没有呈现出来,是因为查询数据接口还没有完善,下面针对查找信息接口进行完善:
(1)接口地址:

接口地址正确,没问题。
(2)接口函数:

目前接口函数还是最开始我们学习接口路由时,测试使用user表的数据模型,与现在接口不匹配,所以我们要整改一下:

此时,编译测试,没问题。

在admin端页面进入分类列表页面:

接口调用成功,但没有成功后的数据。检查原因:

我们的接口传值为categories,但是表名和参数判断为category,所以把接口传递的参数改为category即可:

保存刷新admin端,测试还是不行,原来是因为没有将获取的数据以json格式返回:

重新编译进行测试:

查询所有数据成功,没问题。

3.CRUD接口中的删除数据

检查接口:

优化删除接口:

由于删除字段的命令相同,不需要用到模型,所以免去了if-else判断模型字段的过程。
编译运行,测试:



此时可以看到过程都没问题,但是我们想删除的字段并没有删除成功,查看运行结果:

原因是web端中id值没有传成功,也就是没有找到该条信息中的id。
同时我也意识到之前的查询接口中,也没有成功查询到id字段:

经短时间查找原因,发现一个大BUG,虽然我们插入字段的过程中mongodb会自动生成_id,所以不需要在模型中定义id字段,但我们查询数据是需要通过模型进行查询的:

于是我们需要在模型中添加一个id字段:

保存编译,刷新页面测试:

_id字段就查询出来了,同时上方ID也显示。
此时再测试删除字段:

旭,居然还在:

检查问题!查看手册!!百度!!!!
原因是数据库中的id是个bson格式,而我们传来的id是string字符串格式,所以使用id字段需要把它变成bson格式:

我就不信,这次一定可以!
编译执行测试:
还!是!不!可!以!!!
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊,接着查:

hehe,居然又是传参错误,改成category。

测试:

成功了,可以,研究一下午终于成了。

4.CRUD接口中的单条查询和修改数据

我们修改分类功能复用添加分类功能的组件categorySet.vue组件:

修改组件:

<template>
<div>
<h1>创建分类</h1>
<el-form label-width="80px" style="margin-top:20px;" @submit.native.prevent="save">
<!-- submit事件中使用提交方法save,native是找到js原生表单,prevent是指阻止提交时默认跳转页面 -->
<el-form-item label="分类名">
<el-input v-model="model.name"></el-input>
<!-- 双向绑定model -->
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data(){
return {
model: {}
}
},
methods: {
// 使用async..await异步请求方法
async save(){
let res
// 这里放axios方法,用来提交数据
if(this.id){
// 传id值,表明修改哪一条数据
res = await this.$http.put('rest/categories/' + this.id, this.model)
}else{
res = await this.$http.post('rest/categories', this.model)
}
this.$router.push('/categories/list')
// 提示信息,使用vue中自带的$message实例
this.$message({
type: 'success',
message: '保存成功'
})
},
async fetch(){
const res = await this.$http.get('rest/categories/' + this.id)
this.model = res.data
},
},
created(){
this.id && this.fetch()
}
}
</script>

package db

import(
"github.com/gin-gonic/gin"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"net/http"
"strings"
// "fmt"

// 引入模型
"server/models"
)

// 插入数据
func insert(ctx *gin.Context){

// 解析api参数
resource := ctx.Param("resource")

// 连接mongodb服务
url := "mongodb://127.0.0.1"
// 设置数据库一致性模式
// 连接数据库操作,该操作赋值给session
// err值必写,用于错误处理
session, err := mgo.Dial(url)
// 后边程序执行的err与go程序比对,若有错误则返回错误内容
if err != nil {
panic(err)
}else{
// 若没有错误,则在页面返回字符串,显示插入成功
ctx.String(http.StatusOK, "插入成功")
}
// defer用法大家自行百度,我解释不清
defer session.Close()

// 设置数据库一致性模式,就当作打开数据库
session.SetMode(mgo.Monotonic, true)
// 找到某数据库下的某数据表
c := session.DB("db_go").C(resource)
// 以上为连接数据库

if resource == "user" {
type modelName models.User
// 使用user数据模型
var form modelName

// 如果传值格式不符合上方定义的结构,则返回错误信息
if err := ctx.Bind(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 此时&form就继承了上方定义的结构格式
// 插入数据,并将insert状态传值给err
err = c.Insert(&form)
} else if resource == "category" {
type modelName models.Category
// 使用user数据模型
var form modelName

// 如果传值格式不符合上方定义的结构,则返回错误信息
if err := ctx.Bind(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// 此时&form就继承了上方定义的结构格式
// 插入数据,并将insert状态传值给err
err = c.Insert(&form)
}
// ctx.String(http.StatusOK, fmt.Sprintf(resource))
}

// 查询数据
func find(ctx *gin.Context){

// 解析api参数
resource := ctx.Param("resource")
id := ctx.Param("id")
// 传来的id值为"/id",我们要把"/"截去
id = strings.Trim(id, "/")
// 将id转化为bson.ObjectId格式
var _id bson.ObjectId
_id = bson.ObjectIdHex(id)

url := "mongodb://127.0.0.1"
session, err := mgo.Dial(url)
if err != nil {
panic(err)
}
defer session.Close()

session.SetMode(mgo.Monotonic, true)
c := session.DB("db_go").C(resource)
// 以上为数据库连接

if resource == "user" {
type modelName models.User

modelNamer := modelName{}
// 查找数据
err = c.Find(bson.M{"_id": _id}).One(&modelNamer)
ctx.JSON(http.StatusOK, modelNamer)
} else if resource == "category" {
type modelName models.Category

modelNamer := modelName{}
// 查找数据
err = c.Find(bson.M{"_id": _id}).One(&modelNamer)
ctx.JSON(http.StatusOK, modelNamer)
}


}

// 查询全部数据
func findAll(ctx *gin.Context){

// 解析api参数
resource := ctx.Param("resource")

url := "mongodb://127.0.0.1"
session, err := mgo.Dial(url)
if err != nil {
panic(err)
}
defer session.Close()

session.SetMode(mgo.Monotonic, true)
c := session.DB("db_go").C(resource)

if resource == "user" {
// 使用user数据模型
type modelName models.User
// 查找10条数据
modelNames := make([]modelName, 10)

// 查找全部
err = c.Find(nil).All(&modelNames)

// 返回数据
ctx.JSON(http.StatusOK, modelNames)
} else if resource == "category" {
// 使用user数据模型
type modelName models.Category
// 查找10条数据
modelNames := make([]modelName, 10)

// 查找全部
err = c.Find(nil).All(&modelNames)

// 返回数据
ctx.JSON(http.StatusOK, modelNames)
}
}

// 删除数据
func delete(ctx *gin.Context){

// 解析api参数
resource := ctx.Param("resource")
id := ctx.Param("id")
// 传来的id值为"/id",我们要把"/"截去
id = strings.Trim(id, "/")
// 将id转化为bson.ObjectId格式
var _id bson.ObjectId
_id = bson.ObjectIdHex(id)

url := "mongodb://127.0.0.1"
session, err := mgo.Dial(url)
if err != nil {
panic(err)
}else{
ctx.String(http.StatusOK, id)
}
defer session.Close()

session.SetMode(mgo.Monotonic, true)
c := session.DB("db_go").C(resource)

// 根据获取到的id删除内容
err = c.Remove(bson.M{"_id": _id})

}

// 修改数据
func update(ctx *gin.Context){

// 解析api参数
resource := ctx.Param("resource")
id := ctx.Param("id")
// 传来的id值为"/id",我们要把"/"截去
id = strings.Trim(id, "/")
// 将id转化为bson.ObjectId格式
var _id bson.ObjectId
_id = bson.ObjectIdHex(id)

url := "mongodb://127.0.0.1"
session, err := mgo.Dial(url)
if err != nil {
panic(err)
}else{
ctx.String(http.StatusOK, "修改成功")
}
defer session.Close()

session.SetMode(mgo.Monotonic, true)
c := session.DB("db_go").C(resource)
// 以上为连接数据库

if resource == "user" {
type modelName models.User
// 使用user数据模型
var form modelName
// 合并数据,如果传值格式不符合上方定义的结构,则返回错误信息
if err := ctx.Bind(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 如果传值格式不符合上方定义的结构,则返回错误信息
err = c.Update(bson.M{"_id": _id}, &form)
ctx.JSON(http.StatusOK, &form)
} else if resource == "category" {
type modelName models.Category
// 使用user数据模型
var form modelName
// 合并数据,如果传值格式不符合上方定义的结构,则返回错误信息
if err := ctx.Bind(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// 如果传值格式不符合上方定义的结构,则返回错误信息
err = c.Update(bson.M{"_id": _id}, &form)
ctx.JSON(http.StatusOK, &form)
}

}

// 处理跨域请求,将跨域请求函数作为中间件处理
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method

c.Header("Access-Control-Allow-Origin", "*") // 允许访问所有域,可以换成具体url,注意仅具体url才能带cookie信息
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token") //header的类型
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") //允许请求方法
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") //返回数据格式
c.Header("Access-Control-Allow-Credentials", "true") //设置为true,允许ajax异步请求带cookie信息

//放行所有OPTIONS方法
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// 处理请求
c.Next()
}
}

// 配置路由
func Main(e *gin.Engine) {
// 全局使用中间件
e.Use(Cors())
// 定义路由,调用接口函数
// 增
e.POST("/admin/api/rest/:resource", insert)
// 删
e.DELETE("/admin/api/rest/:resource/*id", delete)
// 改
e.PUT("/admin/api/rest/:resource/*id", update)
// 查
e.GET("/admin/api/rest/:resource", findAll)
// 根据id查某数据
e.GET("/admin/api/rest/:resource/*id", find)
}

大家学习过程有任何疑问可以留言,我研究好后进行回复。
下篇文章我们开始研究mongodb数据库特有的关联方法(无限级分类)。

更多设计、功能的学习经验,大家也可以去我的公众号查看!

————