Node.js简单实现后端服务器
该示例没有用到数据库,是通过创建POST表单路由进行处理数据,将数据保存到文件中,以及数据校验
用到的npm包/第三方模块:
Express:npm i express@4.17.1
– Web 开发框架,快速创建 Web 服务器
Joi:npm install joi --save
– hapijs 自带的数据校验模块
@escook/express-joi中间件:npm i @escook/express-joi
– 实现自动对表单数据进行验证的功能
cors:npm i cors
– 配置cors跨域
dayjs:npm install dayjs --save
– 格式化时间
全局安装了nodemon工具,可以监听文件的更改,自动重新执行
准备工作
- 如果是拿被别人写好的项目,第一步是导入这个项目所用的包,
npm i
命令 - 创建后端项目文件夹如serve
- 初始化文件夹
npm init -y
在空项目的根目录中初始化一个package.json的包管理配置文件 - 安装必要的包/依赖
安装Express ——npm i express@4.17.1
- 创建服务器文件,命名如app.js
创建简单的web服务器 及其 模块化
创建基本的express框架 – eser(vscode快捷生成代码//自己配置 – 文件–首选项–配置用户代码片段)
1
2
3
4
5
6
7
8
9
10//导入express模块
const express = require('express')
//创建express服务器实例
const app = express()
const port = 8080;
//调用app.listen方法,指定端口号并启动web服务器
app.listen(port,()=>{
console.log(`Express server running at http://127.0.0.1:${port}`)
})创建路由
可以先在当前文件尝试是否成功,之后再分离到对应模块1
2
3
4
5
6
7
8
9//创建express路由实例
const router = express.Router()
// 配置路由
app.use(router)
// 提交预约表单
router.post('/form', (req, res) => {
console.log(req.body);
res.send('发送表单数据成功')
})使用postman测试请求是否成功
向 http://127.0.0.1:8080/form 发送post请求
响应函数中打印出来的req.body结果是undefined,这是因为我们没有配置解析表单的中间件配置解析表单的中间件
// 配置解析表单数据的中间件,注意:这个中间件,只能解析applicaton/x-www-form-urlencoded格式的表单数据
app.use(express.urlencoded({extended:false}))
再次发送请求,我们就得到了表单提交的数据分离到路由模块
创建router文件夹,创建如user.js文件//处理用户路由
将路由部分写到这个地方,然后把router暴露出去
module.exports = router
然后在app.js中导入并使用路由模块1
2
3
4// 导入并使用用户路由模块
const userRouter = require('./router/user')
// 配置路由
app.use(userRouter)测试成功
分离处理函数到路由处理模块,同5操作
创建router_handler文件夹,在该目录下创建对应的用户路由处理文件user.js
把处理函数暴露出去1
2
3
4exports.formSubmit = (req, res) => {
console.log(req.body);
res.send('发送表单数据成功')
}在路由模块导入导入用户路由处理函数对应的模块
const user_handler = require('../router_handler/user')
路由的响应函数换成对应的user_handler.formSubmit
数据校验
这里使用joi模块来进行数据验证
Joi 是 hapijs 自带的数据校验模块,他已经高度封装常用的校验功能。
安装joi包,为表单中携带的每个数据项,定义验证规则
1
2
3npm install joi --save
// ES6写法 import Joi from 'joi'
const joi = require('joi')安装 @escook/express-joi 中间件,来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
joi验证规则
- string() 值必须是字符串
- number() 值必须是数字
- alphanum() 值只能是包含 a-zA-Z0-9 的字符串
- min(length) 最小长度
- max(length) 最大长度
- required() 值是必填项,不能为 undefined
- pattern(正则表达式) 值必须符合正则表达式的规则
- any() 任意
- date() 日期
- time() 时间
- iso() 要求字符串值采用有效的 ISO 8601 日期格式。
新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 导入joi模块
const joi = require('joi')
// 表单校验规则
const unit = joi.string().required()
const contact = joi.string().required()
const phone = joi.string().pattern(/^[1][\d]{10}$/).required()
const numVisitors = joi.number().min(16).required()
const guide = joi.string().pattern(/yes|no|是|否/).required()
const date = joi.date().min('now').iso().required()
const time = joi.string().pattern(/^[\d]{2}[\s]*:[\s]*[\d]{2}$/).required()
// 表单的验证规则对象
exports.form_schema ={
body:{
unit,
contact,
phone,
numVisitors,
guide,
date,
time,
}
}在路由模块添加数据校验
1
2
3
4// 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 导入需要验证的规则
const {form_schema} = require('../schema/user')在表单提交的路由中,声明局部中间件,对当前请求中携带的数据进行验证
数据验证通过后,会把这次请求流转给后面的路由处理函数
数据验证失败后,终止后续代码的执行,并抛出一个全局的 Error 错误,进入全局错误级别中间件中进行处理
router.post('/form',expressJoi(form_schema), user_handler.formSubmit)
在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端
在路由注册之后声明错误级别中间件,注意这里需要在app.js导入joi1
2
3
4
5
6
7
8const joi = require('joi')
// 错误中间件
app.use(function(err,req,res,next){
//数据验证失败
if(err instanceof joi.ValidationError) return res.send(err.message)
// 未知错误
res.send(err.message)
})postman测试成功
配置cors 跨域
之前我们都是在postman发送请求测试,如果我们在自己写的页面发送请求会发现报错
这是因为我们的页面打开的是file协议,而服务器则是http协议,
使用cors中间件解决跨域问题
cors 是Express的一个第三方中间件。通过安装和配置cors 中间件,可以很方便地解决跨域问题。
使用步骤分为如下3步:
1. 运行npm install cors
安装中间件
2. 使用const cors = require('cors')
导入中间件
3. 在路由之前调用app.use(cors())
配置中间件
注意:一定要在路由之前配置cors中间件,从而解决接口跨域的问题
- 前端发送axios请求:注意:axios 使用 post 发送数据时,默认是直接把 json 放到请求体中提交到后端的,然而我们没有配置解析JSON表单数据的中间件,所以后端能够接收到请求,但是拿不到参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19axios({
method: 'POST',
url: 'http://127.0.0.1:8080/form',
// 如果要携带请求体:
// 请求体参数,post请求体传参,可选
data: {
unit : "00",
contact : "xxx",
phone: '12345678901',
numVisitors: 25,
guide: 'yes',
date: '2025-05-15',
time: '14:30'
}
}).then((result) => {
// .then 用来指定请求成功之后的回调函数
// 形参中的 result 是请求成功之后的结果
console.log(result.data);
})
配置解析JSON格式表单数据的中间件:app.use(express.json())
再次在页面发送axois请求,测试成功
处理数据 – date时间格式化
在路由处理模块,我们通过
const userInfo = req.body
接收了获取的数据
但是打印userInfo出来发现,date: 2025-05-15T00:00:00.000Z,
,和我们传入的2025-05-15
并不一致
遍历对象打印date又变成了:date: Thu May 15 2025 08:00:00 GMT+0800 (中国标准时间)
这也许是因为数据校验调用了date()导致const date = joi.date().min('now').iso().required()
所以我们需要手动处理一下接收到的date的格式
下载dayjs组件库,并引入到路由处理模块
1
2
3
4
5// 下载 dayjs 组件库
npm install dayjs --save
// 进行引用
const dayjs = require('dayjs')处理data
我们可以通过forEach遍历,并打印出每一项的数据(需要把对象的键值对包装成数组)1
2
3
4
5
6
7
8// entries()方法 把对象中的 每个键值对 包装成一对数组 最后返回一个数组
// 返回如[ [key1,value,],[key2,value2] ,.. ]
Object.entries(userInfo).forEach((item)=>{
if (item[0] == 'date'){
item[1] = dayjs(item[1]).format('YYYY-MM-DD');
}
console.log(item[0] + ": " + item[1]);
})date数据处理完毕
将数据保存到文件中
导入fs文件处理模块
1
const fs = require('fs')
向form.txt文件中追加每一项数据,不需要我们手动去创建txt文件,如果没有会自动创建
1
2
3
4
5
6// 这里的路径,是以执行的文件为基准的,这里是暴露出去的函数,是在路由里面执行的
// 而路由是注册在app.js文件的,所以以app.js为基准
//这里是追加内容的函数,和写入函数writeFile使用一样,如果该文件不存在则会创建该文件
fs.appendFile('./form.txt',data, (err)=>{
if(err) return err.message;
})这样就成功向form.txt文件写入数据了,但是数据是没有换行的
写入数据换行 – 换行符常量
os.EOL
先引入os模块:const os = require('os')
然后修改追加的data数据1
2
3
4
5// 这里插入数据依旧插入的英文名称,没有进行更改对应中文处理
let data = item[0] + ": " + item[1] + os.EOL;
if (index == 0) {// 每一次插入前,都多空上一行 和加上一行分隔符
data = os.EOL + "####预约申请表####" + os.EOL + data;
}
增加msg路由模块
比form路由简单些,不需要进行太多数据验证,只需要保证数据不为空就行
这里直接贴出代码
路由模块 msg.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 导入express模块,并创建路由实例
const express = require('express');
const msgRouter = express.Router();
// 导入路由处理模块
const msg_hander = require('../router_handler/msg')
// 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 导入数据校验模块
const { msg_schema } = require('../schema/msg')
msgRouter.post('/msg',expressJoi(msg_schema),msg_hander.msgSubmit)
module.exports = msgRouter路由处理模块
1
2
3
4
5
6
7
8
9
10
11
12const fs = require('fs')
const os = require('os')
exports.msgSubmit = (req,res) => {
const msg = req.body;
const data = '####留言####' + os.EOL + msg.msg + os.EOL + os.EOL;
// 写入文件
fs.appendFile('./msg.txt',data,(err)=>{
if(err) return err.message;
})
res.send('发送留言数据成功')
}数据校验模块
1
2
3
4
5
6
7
8
9
10// 导入joi模块
const joi = require('joi');
// 校验规则 msg不少于10个字符,且是必填项
const msg = joi.string().min(10).required();
exports.msg_schema = {
body:{
msg,//注意必须写在body属性中,因为数据在req.body中
}
}
注意:记得要在app.js主程序页面配置路由app.use(msgRouter)