
文章目录
源码链接(免积分、不付费)
初始化Node.js项目
1.创建项目
- 创建项目文件夹,并再项目根目录初始化npm包管理文件,以ExpressServer为例,代码如下:
- mkdir ExpressServer && cd ExpressServer
- npm init -y
复制代码- //1. 导入express模块
- const express = require('express')
- //2. 创建express服务器实例
- const server = express()
- // TODO:在这里编写服务器代码
- //3. 监听服务器80端口
- server.listen(80, () => {
- console.log('Express server running on http://127.0.0.1:80')
- })
复制代码 2.配置cors跨域
- //1. 导入cors中间件
- const cors = require('cors')
- //2. 注册cors中间件
- server.use(cors())
复制代码 3.配置表单数据解析中间件
- 配置如下代码,解析application/x-www-form-urlencoded格式的表单数据:
- server.use(express.urlencoded({ extended: false }))
复制代码 4.初始化路由文件夹
- 项目根目录中,创建router文件夹,用于存放所有的路由模块
- 项目根目录中,创建router_handler文件夹,用于存放所有的路由处理模块
- mkdir router
- mkdir router_handler
复制代码 5.初始化路由模块
- 在router文件夹中新建auth.js,用于存储所有的用户路由,编写内容如下:
- const express = require('express')
- //创建路由对象
- const router = express.Router()
- //用户注册路由
- router.post('/register', (req, res) => {
- res.send('POST /register')
- })
- //用户登录路由
- router.post('/login', (req, res) => {
- res.send('POST /login')
- })
- //共享router对象
- module.exports = router
复制代码- //导入用户路由
- const authRouter = require('./router/auth')
- //注册用户路由
- server.use('/api/auth',authRouter)
复制代码 6.启动并测试服务器
- 安装nodemon模块,用于启动服务器(nodemon模块可以在我们修改代码后自动重启服务器):
如果操作正确,服务器正常启动,将输出如下内容:
- PS E:\ExpressServer> nodemon .\app.js
- [nodemon] 2.0.16
- [nodemon] to restart at any time, enter `rs`
- [nodemon] watching path(s): *.*
- [nodemon] watching extensions: js,mjs,json
- [nodemon] starting `node .\app.js`
- Express server running on http://127.0.0.1:80
复制代码
- 使用postman测试接口是否配置正确,以POST方式分别访问localhost/api/auth/register和localhost/api/auth/login


7.抽离路由处理函数
为了保证路由模块的存粹性,将路由处理函数单独抽离出来放在router_handler文件夹中
- 在router_handler文件夹中创建并编辑auth.js文件如下:
- //router_handler/auth.js
- //注册处理函数
- exports.authRegister = (req, res) => {
- res.send('POST /register')
- }
- //登录处理函数
- exports.authLogin = (req, res) => {
- res.send('POST /login')
- }
复制代码- //router/auth.js
- const express = require('express')
- //创建路由对象
- const router = express.Router()
- //引入auth处理模块
- const authHandler = require('../router_handler/auth')
- //用户注册路由
- router.post('/register', authHandler.authRegister)
- //用户登录路由
- router.post('/login', authHandler.authLogin)
- //共享router对象
- module.exports = router
复制代码
- 使用nodemon启动并使用postman访问localhost/api/auth/register和localhost/api/auth/login,会得到和之前(6.3)相同的结果。
注册
1.创建数据库
- CREATE SCHEMA `db_node` ;
复制代码 如果还没有安装MySql,可以在这里下载MySql安装器
- CREATE TABLE `db_node`.`t_users` (
- `id` INT NOT NULL AUTO_INCREMENT,
- `username` VARCHAR(255) NOT NULL,
- `password` VARCHAR(255) NOT NULL,
- `nickname` VARCHAR(255) NULL,
- `email` VARCHAR(255) NULL,
- `avatar` TEXT NULL,
- PRIMARY KEY (`id`),
- UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,
- UNIQUE INDEX `username_UNIQUE` (`username` ASC) VISIBLE)
- ENGINE = InnoDB
- DEFAULT CHARACTER SET = utf8mb4
- COLLATE = utf8mb4_0900_ai_ci;
复制代码 2.安装并配置mysql模块
- 创建/db/index.js文件,此文件用于存储数据库连接对象:
- // db/index.js
- //导入mysql模块
- const mysql = require('mysql')
- //创建数据库连接对象
- const db = mysql.createPool({
- host: '127.0.0.1', //数据库服务器地址,我们使用本机
- user: 'root', //mysql用户名,替换为你的用户名
- password: '000000', //mysql密码,替换为你的密码
- database: 'db_node',//数据库名称,此处为db_node
- })
- //导出数据库连接对象
- module.exports = db
复制代码 3.注册
- 检测表单数据
- 检测用户名是否占用
- 密码加密处理
- 插入新用户
- const userinfo = req.body
- if(!userinfo.username || !userinfo.password){
- return res.send({
- status: 201,
- message:'用户名、密码不能为空!'
- })
- }
复制代码
- const db = require('../db/index')
复制代码
- sqlStr = 'select * from t_users where username = ?'
复制代码
- db.query(sql, userinfo.username, (err, results) => {
- if (err) return res.send({ status: 201, message: err.message })
- if(results.length > 0){
- return res.send({status:201,message:'用户名已存在'})
- }
- //TODO:插入新用户
- })
复制代码
- 在router_handler/auth.js中,导入bcryptjs
- const bcrypt = require('bcryptjs')
复制代码
- 插入用户之前,使用bcrypt.hashSync(password,len)进行加密
- userinfo.password = bcrypt.hashSync(userinfo.password,10)
复制代码
- sqlStr = 'insert into t_users set ?'
复制代码
- db.query(sql, { username: userinfo.username, password: userinfo.password }, (err, results) => {
- if (err) return res.send({ status: 201, message: err.message })
- if (results.affectedRows === 1)
- return res.send({ status: 200, message: 'success' })
- return res.send({ status: 201, message: '注册失败,稍后再试' })
- })
复制代码 4.注册测试


如此,注册方法变成功执行了。
优化res.send()
我们在代码中多次使用到了res.send()方法,非常繁琐,需要封装简化代码。(不优化也没啥)
- 在app.js中所有的路由之前定义并注册全局中间件
- server.use((req, res, next) => {
- //status = 200 success
- //status = 201 failure
- res.cc = function (err, status = 1) {
- res.send({
- status: status,
- message: err instanceof Error ? err.message : err,
- })
- }
- next()
- })
复制代码 这样在所有的路由中,res都具有一个cc方法,可以方便的向客户端输出结果。
优化表单验证
表单验证,前端为辅,后端为主,永远不相信前端提交的数据
1.安装包
1.安装joi包,为表单项定义验证规则
- 安装@escook/express-joi,实现自动验证表单数据
- npm i @escook/express-joi
复制代码 2.验证规则
- mkdir schema
- touch schema/auth.js
复制代码- //schema/auth.js
- //导入包
- const joi = require('joi')
- /**
- * string() 字符串
- * alphanum() 字符数字串
- * min(len) 最短
- * max(len) 最长
- * required() 不为空
- * pattern(reg) 符合正则
- */
- //用户名密码验证规则
- const username = joi.string().alphanum().min(1).max(10).required()
- const password = joi.string().pattern(/^[\S]{6,12}$/).required()
- //登录注册验证对象
- exports.reg_login_schema = {
- body: {
- username,
- password
- }
- }
复制代码 4.插入验证中间件
- //引入验证中间件
- const expressJoi = require('@escook/express-joi') //(*)
复制代码- //引入验证规则
- const { reg_login_schema } = require('../schema/auth')//(*)
复制代码- //用户注册路由,添加验证中间件
- router.post('/register', expressJoi(reg_login_schema), authHandler.authRegister) //(*)
复制代码 修改后的route/auth.js,如下:
- //router/auth.jsconst express = require('express')//创建路由对象const router = express.Router()//引入验证中间件
- const expressJoi = require('@escook/express-joi') //(*)//引入验证规则
- const { reg_login_schema } = require('../schema/auth')//(*)//引入auth处理模块const authHandler = require('../router_handler/auth')//用户注册路由,添加验证中间件
- router.post('/register', expressJoi(reg_login_schema), authHandler.authRegister) //(*)//用户登录路由router.post('/login', authHandler.authLogin)//共享router对象module.exports = router
复制代码 注意以上代码中(*)处是修改的地方。
5.捕获验证错误
在app.js中创建并注册全局错误处理中间件,用于处理验证错误(也可以处理其他错误)。
- //引入验证规则模块
- const joi = require('joi')
复制代码- //引入验证规则模块
- const joi = require('joi')//注册异常捕获中间件server.use((err, req, res, next) => { if (err instanceof joi.ValidationError) return res.cc(err) res.cc(err)})
复制代码 6.验证测试

登录
1.登录步骤
- 表单验证;
- 数据查询;
- 密码比较;
- 生成JWT的Token字符串
2.表单验证
- //用户登录路由
- router.post('/login', expressJoi(reg_login_schema), authHandler.authLogin)
复制代码 3.数据查询
在router_handler/auth.js中的登录处理方法中:
- const userinfo = req.body
复制代码- const sqlStr = 'select * from t_users where username=?'
复制代码- //执行查询
- db.query(sqlStr, userinfo.username, (err, results) => {
- //查询失败
- if (err) return res.cc(err)
- //查询结果不合理
- if (results.length !== 1) return res.cc("登录失败")
- //TODO:判断密码
- })
复制代码 4.密码比较
调用bcrypt.compreSync(表单密码,数据库密码)判断密码是否一致,true一致,false不一致
- //判断密码
- const cmpRes = bcrypt.compare(userinfo.password, results[0].password)
- if (!cmpRes) return res.cc('Login Failed')
- //TODO:登录成功,生成token
复制代码 5.生成token
- 从查询结果中剔除password和avatar两个值
- const usr = { ...results[0], password: '', avatar: '' }
复制代码
- 在router_handler/auth.js中导入jwt
- const jwt = require('jsonwebtoken')
复制代码
- 根目录创建配置文件config.js,并共享jwtSecretKey字符串(用于加密)
- //config.js
- module.exports = {
- //一个复杂字符串
- jwtSecretKey: "alkjflasngaoieakgbnasdfzxfgasdf",
- expiresIn: '24h',//token有效期24h
- }
复制代码- //导入config
- const config = require('../config')
- //生成token
- const tokenStr = jwt.sign(usr, config.jwtSecretKey, {
- expiresIn: config.expiresIn,//token有效期为24小时
- })
复制代码- res.send({
- status: 200,
- message: 'login success',
- token: 'Bearer ' + tokenStr
- })
复制代码 
6.Token解析
- 安装express-jwt模块(注意版本,较新版本不适合本教程)
- const config = require('./config')
- //导入token中间件
- const expressJWT = require('express-jwt')
- //注册token中间件,所有以/api开头的路由都需要验证token的正确性
- server.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api\//] }))
复制代码- if (err.name === 'UnauthorizedError') return res.cc('认证失败')
复制代码 访问非/api开头的路由即可,这里使用/my

想要验证成功,需要在Header中加入Authorization字段,字段的值是登录时返回的Token:

获取用户信息
用户中心功能的一部分,获取用户个人信息,使用/my/info路由的GET请求。
1.基本步骤
- 初始化个人中心路由“router/my.js”
- 初始化路由处理函数“router_handler/my.js”
- 获取用户信息
2.初始化/router/my.js
- // 引入express模块
- const express = require('express')
- //创建路由对象
- const router = express.Router()
- //挂接/info路由
- router.get('/info', (req, res) => {
- res.send('ok')
- })
- //向外分享路由对象
- module.exports = router
复制代码
- 在app.js中导入当前路由模块(可以紧贴/api/auth路由向后写)
- //导入路由
- const myRouter = require('./router/my')
- //注册/my路由
- server.use('/my', myRouter)
复制代码 3.测试路由配置
- 访问/api/auth/login登录并获取token

复制返回的token字符串。

如果你和我的编码相同,返回ok即为正确。
4.初始化/router_handler/my.js
- 新建/router_handler/my.js文件,编辑内容如下:
- //暴露/my/info路由的处理函数
- exports.getInfo = (req, res) => {
- res.send('/my/info handler')
- }
复制代码- // 引入express模块
- const express = require('express')
- //创建路由对象
- const router = express.Router()
- const myHandler = require('../router_handler/my') //(*)
- //挂接/info路由
- router.get('/info', myHandler.getInfo)//(*)
- //向外分享路由对象
- module.exports = router
复制代码 注意上述代码中(*)处的改动。

如果返回的内容和我一样就没有问题了。
5.获取用户信息
- const db = require('../db/index')
复制代码- const sql = 'select id,username,nickname,email,avatar from t_users where id=?'
复制代码- db.query(sql, req.user.id, (err, results) => {
- if (err) return res.cc(err.message)
- if (results.length !== 1) return res.cc('获取用户信息失败')
- res.send({
- status: 200,
- message: 'Success',
- data: results[0]
- })
- });
复制代码 6.测试

这样就执行成功了。
更新用户信息
使用/my/info的POST请求更新用户个人信息。
1.实现步骤
2.定义路由和处理函数
- //更新信息
- router.post('/info', myHandler.setInfo)
复制代码- //router_handler/my.js
- //设置用户信息
- exports.setInfo = (req, res) => {
- res.rend('post /my/info handler')
- }
复制代码 
3.参数验证
新建/schema/my.js文件,编辑如下:
- //schema/my.js
- //导入验证规则模块
- const joi = require('joi')
- //定义验证规则
- const id = joi.number().integer().min(1).max(10).required()
- const nickname = joi.string().required()
- const email = joi.string().email().required()
- //导出验证规则
- exports.update_info_schema = {
- body: {
- id,
- nickname,
- email
- }
- }
复制代码 在/router/my.js中引入express-joi中间件
- // 引入验证中间件
- const express_joi = require('@escook/express-joi')
复制代码 在/router/my.js中引入joi验证规则
- // 引入验证规则
- const { update_info_schema } = require('../schema/my')
复制代码- //更新信息
- router.post('/info', express_joi(update_info_schema), myHandler.setInfo)
复制代码 
如果我们不提供id参数,就会报如下错误。
如果我们提供正确的参数,将获得如下结果:

源码链接(免积分、不付费)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |