Contents
15. 通用API开发¶
15.1. 1.获取页面导航栏¶
接口路由地址为:http://localhost:3000/getNavMenu
展示型网站的首页一般都有导航标题栏,单击导航栏菜单,会跳转到一个新的页面(可能是分类或其他连接)。
导航栏一般存在多级菜单,本例为了方便,仅展示一级菜单。导航栏存放在数据库中的格式如下方的JSON字符串所示。
通过本接口获取的数据基本格式如下:
[{
"name": "主页",
"src": "http://loaclhost"
}, {
"name": "文章",
"src": "/article/list"
}]
注意:后端接口本身保存的就是JSON字符串,所以实现多级菜单是非常简单的,嵌套对象即可,前端同样也要实现多级菜单才可以正常显示。
通过redis-cli工具可实现数据操作,使用set命令手动添加数据,同时指定其键名为book:nav_menu。
导航栏路由建立在router文件夹的index.js文件中,为了避免大量的路由和逻辑导致程序编写混乱,应单独将逻辑处理部分提取出来,通过引入的方式进行应用。
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getNavMenu} = require('../controller/getData')
const util = require('../util/common')
// 获取footer显示内容
router.get('/getFooter', getFooter);
//获取菜单
router.get('/getNavMenu', getNavMenu);
访问导航栏路由后会调用getNavMenu()方法,接下来编写该方法。在项目文件中新建controller文件,在其中添加getData.js文件并在该文件中编写所有获取数据的代码逻辑,具体如下:
let redis = require("../util/redisDB")
const util = require('../util/common')
const crypto = require('crypto');
exports.getNavMenu = (req, res, next) => {
let key = req.headers.fapp + ":nav_menu"
//获取数据
redis.get(key).then((data) => {
console.log(data)
res.json(util.getReturnData(0, '', data))
})
}
Postman中进行测试.返回如下数据:
{
"code": 0,
"message": "",
"data": [
{
"name": "主页",
"src": "http://loaclhost"
},
{
"name": "文章",
"src": "/article/list"
}
]
}
15.2. 2.获取底部详细内容¶
接口路由地址为:http://localhost:3000/getFooter
通过本接口获取的数据基本格式如下:
[
{
"name": "版权所有",
"src": "http://loaclhost",
"text": "Stiller"
},
{
"name": "发送邮件",
"src": "mailto:1879324764@qq.com",
"text": "Gmail"
}
]
首先在routers/index.js中编写相应的路由地址,修改后的index.js代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getFooter} = require('../controller/getData')
const util = require('../util/common')
// 获取footer显示内容
router.get('/getFooter', getFooter);
module.exports = router;
接着在controller/getData.js文件中添加getFooter对象,代码如下:
//获取Footer相关内容
exports.getFooter = (req, res, next) => {
let key = req.headers.fapp + ":footer"
//获取数据
redis.get(key).then((data) => {
console.log(data)
res.json(util.getReturnData(0, '', data))
})
}
最后通过Postman插件就可以获取相应的数据:
{
"code": 0,
"message": "",
"data": [
{
"name": "版权所有",
"src": "http://loaclhost",
"text": "Stiller"
},
{
"name": "发送邮件",
"src": "mailto:1879324764@qq.com",
"text": "Gmail"
}
]
}
15.3. 3.获取友情链接¶
接口路由地址为: http://localhost:3000/getLinks
通过本接口获取的数据基本格式如下:
[{
"name": "baidu",
"src": "http://baidu.com"
}]
首先在routers/index.js中编写相应的路由地址,修改后的index.js代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getLinks} = require('../controller/getData')
const util = require('../util/common')
//获取友情链接
router.get('/getLinks', getLinks)
module.exports = router;
接着在controller/getData.js文件中添加getLinks对象,代码如下:
//获取友情链接
exports.getLinks = (req, res, next) => {
let key = req.headers.fapp + ":links"
//获取数据
redis.get(key).then((data) => {
console.log(data)
res.json(util.getReturnData(0, '', data))
})
}
最后通过Postman插件就可以获取相应的数据
{
"code": 0,
"message": "",
"data": [
{
"name": "baidu",
"src": "http://www.baidu.com"
}
]
}
15.4. 4.获取首页轮播图¶
接口路由地址为: http://localhost:3000/getIndexPic
通过本接口获取的数据基本格式如下:
[{
"title": "baidu",
"src": "http://www.baidu.com",
"img": "https://www.bejson.com/static/bejson/img/logo.png"
}]
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getIndexPic} = require('../controller/getData')
const util = require('../util/common')
//获取首页轮播图片
router.get('/getIndexPic', getIndexPic)
module.exports = router;
接着在controller/getData.js文件中添加getIndexPic对象,代码如下:
//获取首页轮播图片相关内容
exports.getIndexPic = (req, res, next) => {
let key = req.headers.fapp + ":indexPic"
//获取数据
redis.get(key).then((data) => {
console.log(data)
res.json(util.getReturnData(0, '', data))
})
}
最后通过Postman就可以获取相应的数据
{
"code": 0,
"message": "",
"data": [
{
"title": "baidu",
"src": "http://www.baidu.com",
"img": "https://www.bejson.com/static/bejson/img/logo.png"
}
]
}
15.5. 5.获取热点文章列表内容¶
接口路由地址为: http://localhost:3000/getHotArticle
通过本接口获取的数据基本格式如下:
[{
"title": "文章暂未上线",
"data": "",
"id": 0,
"view": 0
}, {
"title": "测试文章3",
"data": "",
"id": 3,
"view": "3"
}]
获取热点文章时返回文章的发布时间、题目和ID,最多5条数据。如果文章已经发布但未上线或已下线,则返回文章暂未上线的提示。
注意:新建文章时,会在Redis中新建名为book:a_view的有序列表,该列表按照时间戳保存文章key。
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getHotArticle} = require('../controller/getData')
const util = require('../util/common')
//获得热点文章列表
router.get('/getHotArticle', getHotArticle)
module.exports = router;
接着在controller/getData.js文件中添加getHotArticle对象,代码如下:
//获取热点文章
exports.getHotArticle = (req, res, next) => {
let key = req.headers.fapp + ":a_view"
//获得集合,只取得0,1,2,3,4五个
redis.zrevrange(key, 0, 4).then(async (data) => {
console.log(data)
let result = data.map((item) => {
//获得每一篇文章的题目和id以及日期
return redis.get(item.member).then((data1) => {
console.log(data1)
if (data1 && data1.show != 0) {
return {
'title': data1.title,
'date': util.getLocalDate(data1.time),
'id': data1.a_id,
'view': item.score
}
} else {
return {'title': '文章暂未上线', 'date': '', 'id': 0, 'view': 0}
}
})
})
let t_data = await Promise.all(result)
res.json(util.getReturnData(0, '', t_data))
})
}
需要注意的是,使用map循环时不可避免地会碰到异步问题。
笔者采用了await/async方式,使用Promise.all等待多个异步执行完毕后再获取汇总结果。如果直接在map后输出相关的值,则无法获取实际的值。
获取热点文章列表,实际上和根据时间获取列表的逻辑一样,不同之处在于对取值的限制,热点文章还需要将获取的数值返回给前端。通过Postman插件可以获取相应的数据
{
"code": 0,
"message": "",
"data": [
{
"title": "文章暂未上线",
"date": "",
"id": 0,
"view": 0
},
{
"title": "文章暂未上线",
"date": "",
"id": 0,
"view": 0
}
]
}
15.6. 6.获取文章列表¶
接口路由地址为: http://localhost:3000/getNewArticle
可以先编写文章添加或修改的API,以提供数据源,使数据库中有了真实的数据后,再返回来编写相应数据获取的逻辑代码。新建文章时会在Redis中创建名为book:a_time的有序列表,该列表按照时间戳保存文章key。
通过本接口获得的数据结构如下,返回的是文章的时间、题目和ID,暂时不提供分页功能,只是一次性返回所有的文章列表。如果文章已经发布但未上线或已下线,则返回文章暂未上线的提示。
{
"code": 0,
"message": "",
"data": [
{
"title": "测试文章3",
"date": "2022-7-20 9:30:5",
"id": 3,
"show": 0
},
{
"title": "测试文章2",
"date": "2022-7-20 9:29:54",
"id": 2,
"show": 0
},
{
"title": "测试文章1",
"date": "2022-7-20 9:29:37",
"id": 1,
"show": 0
}
]
}
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getNewArticle} = require('../controller/getData')
const util = require('../util/common')
//获取最新文章列表
router.get('/getNewArticle', getNewArticle)
module.exports = router;
接着在controller/getData.js文件中添加一个getNewArticle对象,代码如下:
//获取最新的文章列表
exports.getNewArticle = (req, res, next) => {
let key = req.headers.fapp + ":a_time"
let isAdmin = false
//获取数据
console.log(key)
//获得集合
//登录用户才判断
if ('token' in req.headers) {
//如果是管理员,则在加一次查找
let pKey = req.headers.fapp + ":user:power:" + req.headers.token
redis.get(pKey).then((power) => {
//管理员权限
if (power == 'admin') {
redis.zrevrange(key, 0, -1).then(async (data) => {
let result = data.map((item) => {
//获得每一篇文章的题目和id以及日期
return redis.get(item.member).then((data1) => {
console.log(data1)
if (data1) {
return {'title': data1.title, 'date': util.getLocalDate(item.score), 'id': data1.a_id,'show':data1.show}
}
})
})
let t_data = await Promise.all(result)
console.log(t_data)
res.json(util.getReturnData(0, '', t_data))
})
}else{
// res.json(util.getReturnData(1, '其他权限'))
// 其他权限暂时依旧执行普通未登录效果
redis.zrevrange(key, 0, -1).then(async (data) => {
console.log(data)
let result = data.map((item) => {
//获得每一篇文章的题目和id以及日期
return redis.get(item.member).then((data1) => {
if (data1 && data1.show != 0) {
return {'title': data1.title, 'date': util.getLocalDate(item.score), 'id': data1.a_id}
} else {
return {'title': '文章暂未上线', 'date': '', 'id': 0}
}
})
})
let t_data = await Promise.all(result)
res.json(util.getReturnData(0, '', t_data))
})
}
})
} else {
redis.zrevrange(key, 0, -1).then(async (data) => {
console.log(data)
let result = data.map((item) => {
//获得每一篇文章的题目和id以及日期
return redis.get(item.member).then((data1) => {
if (data1 && data1.show != 0) {
return {'title': data1.title, 'date': util.getLocalDate(item.score), 'id': data1.a_id}
} else {
return {'title': '文章暂未上线', 'date': '', 'id': 0}
}
})
})
let t_data = await Promise.all(result)
res.json(util.getReturnData(0, '', t_data))
})
}
}
上述代码通过键值获取有序列表中的值,同时查找这些值的内容,找到相应的文章并且汇总到最终的返回结果中。通过Postman插件可以获取相应的数据
{
"code": 0,
"message": "",
"data": [
{
"title": "测试文章3",
"date": "2022-7-20 9:30:5",
"id": 3,
"show": 0
},
{
"title": "测试文章2",
"date": "2022-7-20 9:29:54",
"id": 2,
"show": 0
},
{
"title": "测试文章1",
"date": "2022-7-20 9:29:37",
"id": 1,
"show": 0
}
]
}
15.7. 7.获取文章详情¶
实现的接口路由地址为:http://localhost:3000/getArticle/:id
可以先编写文章添加或修改的API,以提供数据源,使数据库中有了真实的数据后,再返回来编写相应数据获取的逻辑代码。
本接口返回的应当是文章详情,需要文章的show字段为1,提供一个a_id作为参数,其数据基本格式如下:
{
"code": 0,
"message": "success",
"data": {
"title": "测试文章1",
"writer": "admin",
"text": "测试文章1",
"type": 1,
"tag": [
"js",
"node"
],
"show": 1,
"time": 1658280576995,
"a_id": 1,
"view": "0",
"like": "0"
}
}
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getArticle} = require('../controller/getData')
const util = require('../util/common')
//获取文章的详情
router.get('/getArticle/:id', getArticle)
module.exports = router;
接着编写逻辑处理的controller/getData.js文件,在其中编写一个getArticle对象,除了根据ID获取文章以外,还涉及文章的分类(文章的分类仅是一个唯一的ID),以及有序队列中的点赞数、阅读量的获取,代码如下:
//根据id获取到文章的基本内容
exports.getArticle = (req, res, next) => {
//获取参数
let key = req.headers.fapp + ":article:" + req.params.id
redis.get(key).then((data) => {
// console.log(data)
//判断是否显示文章内容
if (data) {
if (data.show == 1) {
//获取文章分类详情
redis.get(req.headers.fapp + ":a_type").then((type) => {
type.map((item) => {
if (item.uid == data.type) {
data.typename = item.name
}
})
//获取文章的阅读量
redis.zscore(req.headers.fapp + ":a_view", key).then((view) => {
console.log(view)
data.view = view
//获取文章的点赞量
redis.zscore(req.headers.fapp + ":a_like", key).then((like) => {
data.like = like
res.json(util.getReturnData(0, 'success', data))
})
})
})
} else {
res.json(util.getReturnData(403, '该文章已经被删除或者是不存在'))
}
} else {
res.json(util.getReturnData(404, '该文章已经被删除或者是不存在'))
}
})
}
注意,在功能设计中一些没有上线或不存在的文章是无法被访问的,需要提示相应的返回内容。通过Postman插件可以获取相应的数据。
获取文章详情
{
"code": 0,
"message": "success",
"data": {
"title": "测试文章1",
"writer": "admin",
"text": "测试文章1",
"type": 1,
"tag": [
"js",
"node"
],
"show": 1,
"time": 1658280576995,
"a_id": 1,
"view": "0",
"like": "0"
}
}
15.8. 8.获取文章评论¶
接口路由地址是: http://localhost:3000/getArticleTalk/:id
可以先编写文章添加或修改的API,以提供数据源,使数据库中有了真实的数据后,再返回来编写相应数据获取的逻辑代码。采用GET方式获取文章评论的接口,需要传递文章的唯一ID。
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {getArticleTalk} = require('../controller/getData')
const util = require('../util/common')
//获取文章评论
router.get('/getArticleTalk/:id',getArticleTalk)
module.exports = router;
接着编写处理逻辑的controller/getData.js文件,此处需要接收一个id参数,并通过ID查找评论数据,最终将该数据返回给前端。完整的代码如下:
//获得文章评论
exports.getArticleTalk = (req, res, next) => {
let key = req.headers.fapp + ":article:" + req.params.id + ":talk"
redis.get(key).then((data) => {
res.json(util.getReturnData(0, 'success', data))
})
}
传递一篇文章的ID后,返回内容如下
获取文章评论
{
"code": 0,
"message": "success",
"data": [
{
"talk": "这是第一次评论",
"time": 1658280939748,
"username": "admin"
}
]
}
15.9. 9.获取分类内容¶
接口路由地址是: http://localhost:3000/getArticles
可以先编写文章添加或修改的API,以提供数据源,使数据库中有了真实的数据后,再返回来编写相应数据获取的逻辑代码。
为了方便数据的传输,这里定义该接口为POST请求方式,传递的数据参数如下,传递tag时为小标签,传递type时为分类。
注意:对于不更改数据和仅用于查询的接口,建议使用GET方式定义接口。
本项目的所有接口发送数据时均采用JSON方式,在Postman中模拟时,选择Raw选项卡可以编辑JSON。
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var { getArticles} = require('../controller/getData')
const util = require('../util/common')
//获取小标签或者是文章分类的内容
router.post('/getArticles', getArticles)
module.exports = router;
接着在controller/getData.js文件中添加一个getArticles对象,该对象接收两个相关的参数:如果存在type,则以type为准;如果存在tag,则对tag字符串执行MD5算法。
使用如下代码引入crypto包,其中包含了MD5算法,该模块是Node.js自带的,无须安装就可以使用其加密算法。
const crypto = require('crypto');
获取分类的数据列表之后,还需要获取完整的数据,再次使用数组的map循环完成这项工作。完整的代码如下:
//getArticles
//根据小标签或者是分类获得所有的文章
exports.getArticles = (req, res, next) => {
let key = req.headers.fapp
// 筛选,如果是tag则使用md5
if ('tag' in req.body) {
let tKeyMd5 = crypto.createHash('md5').update(req.body.tag).digest("hex")
key = key + ':tag:' + tKeyMd5
console.log(key)
} else if ('type' in req.body) {
//如是type则直接使用id
key = key + ':a_type:' + req.body.type
console.log(key)
} else {
res.json(util.getReturnData(1, '数据参数错误'))
return
}
redis.get(key).then(async (data) => {
console.log(data)
// 获得所有的数据
let result = data.map((item) => {
// 获得每一篇文章的题目和id以及日期
return redis.get(item).then((data1) => {
console.log(data1)
if (data1 && data1.show != 0) {
return {'title': data1.title, 'date': util.getLocalDate(data1.time), 'id': data1.a_id}
} else {
return {'title': '文章暂未上线', 'date': '', 'id': 0}
}
})
})
let t_data = await Promise.all(result)
res.json(util.getReturnData(0, '', t_data))
})
}
指定不同的参数就可以获取相关的文章和内容。如果输入不正确的参数,则返回错误信息
{
"code": 0,
"message": "",
"data": [
{
"title": "测试文章1",
"date": "2022-7-20 9:29:36",
"id": 1
},
{
"title": "测试文章2",
"date": "2022-7-20 9:29:54",
"id": 2
},
{
"title": "文章暂未上线",
"date": "",
"id": 0
}
]
}
15.10. 10.记录文章浏览量¶
接口路由地址是: http://localhost:3000/viewArticle/:id
可以先编写文章添加或修改的API,以提供数据源,使数据库中有了真实的数据后,再返回来编写相应数据获取的逻辑代码。
本小节的查看文章数据其实可以不使用接口,在每次获取文章详情时直接对文章的浏览量执行+1操作即可。笔者之所以提供独立的接口,是为了后续更多功能的实现。
首先在routers/index.js文件中编写相应的路由地址,修改后的index.js文件代码如下:
var express = require('express');
var router = express.Router();
//// 引入了逻辑处理的JavaScript文件(需要注意是否有其他路由使用到其他文件,均需要引入,本处已省略)
var {viewArticle} = require('../controller/getData')
const util = require('../util/common')
//文章被查看数自动+1API
router.get('/viewArticle/:id', viewArticle)
module.exports = router;
接着在controller/getData.js文件中添加viewArticle对象,这里使用Redis提供的zincrby()方法,自动对该值执行+1操作,代码如下:
//浏览量自动加一
exports.viewArticle = (req, res, next) => {
let key = req.headers.fapp + ":article:" + req.params.id
redis.zincrby(req.headers.fapp + ':a_view', key)
res.json(util.getReturnData(0, 'success',))
}
这样,当一次请求成功后,再次获取该文章的详情,会看到文章的浏览量发生了变化。每次请求可以增加一次浏览量
"view": "3",
{
"code": 0,
"message": "success",
"data": {
"title": "测试文章1",
"writer": "admin",
"text": "测试文章1",
"type": 1,
"tag": [
"js",
"node"
],
"show": 1,
"time": 1658280576995,
"a_id": 1,
"view": "3",
"like": "0"
}
}