阅读更多系列文章请访问我的GitHub博客,示例代码请访问这里。
添加各接口路由配置
获取商品列表路由回调函数
查询item_table表中的商品数据后,返回给前台,并将回调函数作为模块导出。
示例代码:/lesson29/router/list.js
const connection = require('../lib/database')
module.exports = async (res, query, post, files) => {
try {
// 查询商品列表
const data = await connection.query(`SELECT * FROM item_table`)
res.writeJson({
error: 0, // error为0则表示接口正常
data // 查询到的商品列表数据
})
} catch (error) {
console.error(error)
res.writeJson({
error: 1, // error为1则表示接口出错
msg: '数据库出错' // 接口的错误信息
})
}
res.end()
}
添加商品路由回调函数
应禁止query语句使用如下写法,容易造成注入攻击。
connection.query(INSERT INTO item_table (title, price, count) VALUES('${title}, ${price} ${count}')
)
此时若用户传入参数如下:
http://localhost:8080/add?title=a’)%3B%20DELETE%20FROM%20item_table%3B%20SELECT%20(1&price=19.8&count=200
就会让服务器执行一个这样的语句:
INSERT INTO item_table (title, price, count) VALUES('a'); DELETE FROM item_table; SELECT ('1', 19.8, 200)
其意义为:
- 插入一个虚假数据
- 删除item_table表中所有数据
- 返回一个虚假数据
这样就会导致item_table表中的所有数据被删除。
为防止注入攻击,可以使用占位符?代替需要插入数据库的参数,第二个数组参数中的3个值会按顺序填充占位符,该方法可以避免大部分注入攻击,如下:
await connection.query(`INSERT INTO item_table (title, price, count) VALUES(?,?,?)`, [title, price, count])
示例代码:/lesson29/router/add.js
const connection = require('../lib/database')
module.exports = async (res, query, post, files) => {
let {
title,
price,
count
} = post
// 判断是否有传参
if (!title || !price || !count) {
res.writeJson({
error: 1,
msg: '参数不合法'
})
} else {
// 将价格和数量转为数字
price = Number(price)
count = Number(count)
// 判断价格和数量是否非数字
if (isNaN(price) || isNaN(count)) {
res.writeJson({
error: 1,
msg: '参数不合法'
})
} else {
try {
// 使用占位符?代替需要插入数据库的参数,第二个数组参数中的3个值会按顺序填充占位符,该方法可以避免大部分注入攻击。
await connection.query(`INSERT INTO item_table (title, price, count) VALUES(?,?,?)`, [title, price, count])
res.writeJson({
error: 0,
msg: '添加商品成功'
})
} catch (error) {
console.error(error)
res.writeJson({
error: 1,
msg: '数据库内部错误'
})
}
}
}
res.end()
}
删除商品路由回调函数
示例代码:/lesson29/router/del.js
const connection = require('../lib/database')
module.exports = async (res, query, post, files) => {
const ID = query.id
if (!ID) {
res.writeJson({
error: 1,
msg: '参数不合法'
})
} else {
await connection.query(`DELETE FROM item_table WHERE ID=${ID}`)
res.writeJson({
error: 0,
msg: '删除成功'
})
}
res.end()
}
添加各接口路由配置
在/router/index.js
中,引用各个接口的配置,并用addRouter方法添加到路由表中,即可在接收到请求时,查找路由并进行处理。
示例代码:/lesson29/router/index.js
const {
addRouter
} = require('../lib/router')
// 添加获取商品列表接口
addRouter('get', '/list', require('./list'))
// 添加商品接口
addRouter('post', '/add', require('./add'))
// 删除商品接口
addRouter('get', '/del', require('./del'))
在主模块中引用路由
在/server.js中,引用router模块,就可以完成整个服务端的配置。
示例代码:/lesson29/server.js
const connection = require('./lib/database')
const http = require('./lib/http')
const router = require('./router')
完成前端功能
在/static/index.html
中,使用jquery为前端页面实现如下功能:
- 显示商品列表
- 添加随机名称、价格、库存的商品
- 删除对应商品
示例代码:/lesson29/server.js
// 查询商品列表的方法
function getList() {
$.ajax({
url: '/list',
dataType: 'json'
}).then(res => {
let html = ``
res.data.forEach((item, index) => {
html += (
`
<tr>
<td>${item.title}</td>
<td>¥${item.price}</td>
<td>${item.count}</td>
<td>
<a data-id="${item.ID}" href="#" class="glyphicon glyphicon-trash">删除</a>
</td>
</tr>
`
)
})
$('tbody').html(html)
});
} getList()
// 点击添加按钮,随机添加一个商品
$('#addBtn').on('click', function () {
$.ajax({
url: '/add',
method: 'post',
data: {
title: `test${Math.floor(Math.random() * 100)}`,
price: Math.floor(Math.random() * 100),
count: Math.floor(Math.random() * 100)
}
})
.then((response) => {
getList()
})
})
// 点击删除按钮
$('tbody').on('click', 'a', function () {
$.ajax({
url: '/del',
data: {
id: $(this).attr('data-id')
}
})
.then((response) => {
getList()
})
})
至此,原生Node.js的项目就全部完成了,这个项目“麻雀虽小五脏俱全”,可以很好地锻炼我们对Node.js的理解和开发能力。
当然从开发的过程也可以看到,使用原生Node.js开发效率较低,实际工作中还是会更多地使用Express、Koa等框架,进一步提高开发效率。