利用node.js搭配express框架写后端接口(一)

Node.js 凭借其高效的非阻塞 I/O 操作、事件驱动架构以及轻量级的特点,成为了开发高性能服务器应用的热门选择。Express 框架作为 Node.js 上最流行的 Web 应用框架之一,以其简洁的 API 和丰富的中间件生态系统,极大地简化了 Web 后端开发流程。本文将引导你如何使用 Node.js 和 Express 框架构建一个简单的后端接口。

目录

一、初始化项目

1.创建项目

2.配置cors跨域

3.配置解析表单数据的中间件

4.初始化路由相关的文件夹

 5.初始化用户路由模块

6.抽离用户路由模块中的处理函数

二、新建数据库表

1.新建数据库

2.新建表

三、安装并配置MySQL模块

1.安装MySQL模块

2.连接数据库

四、登录、注册

1.注册

1.1检测表单数据是否合法

1.2检测用户名是否被占用

1.3对密码进行加密处理

1.4插入新用户

1.5优化res.send()代码

1.6优化表单数据验证

2.登录

 2.1检测登录表单的数据是否合法

2.2根据用户名查询用户的数据

2.3判断用户输入的密码是否正确

2.4生成jwt的token字符串

2.5配置解析token的中间件


一、初始化项目

1.创建项目

新建一个文件夹作为项目的根目录,并在项目根目录中运行如下命令,初始化包的管理配置文件:

npm i express@4.17.1

然后在项目中新建app.js作为整个项目的入口文件,并初始化如下代码:

//导入express
const express = require('express')
//创建服务器对象
const app = express() 
//启动服务器
app.listen(3007, () => {console.log('api server htttp://127.0')
})

2.配置cors跨域

在文件夹的终端运行如下的命令,安装cors中间件:

npm i cors@2.8.5

然后再app.js中导入并配置cors中间件:

//导入并配置cors中间件
const cors = require('cors')
app.use(cors())

3.配置解析表单数据的中间件

通过使用如下代码,配置解析application/x-www-form-urlencoded格式的表单数据的中间件:

//配置解析表单数据的中间件--注意:只能解析application/x-www-from-urlencoded格式的表单数据的中间件
app.use(express.urlencoded({extended:false}))

4.初始化路由相关的文件夹

(1)在项目的根目录中,新建router文件夹,用来存放所有的路由模块。路由模块中,只存放客户端的请求与处理函数之间的映射关系。

 (2)在项目的根目录中,新建router_handler文件夹,用来存放所有的路由处理函数模块,这里专门负责存放每个路由对应的处理函数。

 5.初始化用户路由模块

在router文件夹中,新建user.js文件,作为用户的路由模块,代码如下:

const express = require('express')
//创建路由对象
const router = express.Router()//注册新用户
router.post('/register', (req, res) => { res.send('注册成功')
})//登录
router.post('/login', (req, res) => { res.send('登录成功')
})//导出路由对象
module.exports = router

在app.js中导入并使用该用户路由模块

//导入并注册用户路由模块
const userRouter = require('./routes/user')
app.use('/api', userRouter)

6.抽离用户路由模块中的处理函数

为了保证路由模块的纯粹性,所有的路由处理函数,必须抽离到对应的路由处理函数模块中。

在router_handler/user.js文件中,使用exports对象,分别向外共享如下两个路由处理函数:

//注册用户的处理函数
exports.regUser = (req, res) => {res.send('注册成功')
}
//登录的处理函数
exports.login = (req, res) => {res.send('登录成功')
}

将router/user.js文件中的代码修改如下结构

const express = require('express')
//创建路由对象
const router = express.Router()//打入用户路由处理函数模块
const userHandler = require('./router_handler/user')//注册新用户---原
// router.post('/register', (req, res) => { 
//     res.send('注册成功')
// })
//注册新用户--新
router.post('/register',userHandler.regUser )//登录----原
// router.post('/login', (req, res) => { 
//     res.send('登录成功')
// })
//登录---新
router.post('/login',userHandler.login)//导出路由对象
module.exports = router

二、新建数据库表

在创建库表之前确保已经安装了数据库。

1.新建数据库

2.新建表

在新建的test_mysql数据库中,新建ev_users表 。点击新建查询,输入一下SQL语句

use test_mysql;
CREATE TABLE ev_users
(id INT NOT NULL AUTO_INCREMENT,username VARCHAR(255) NOT NULL,password VARCHAR(255) NOT NULL,nickname VARCHAR(255),email VARCHAR(255),user_pic TEXT,PRIMARY KEY (id)
)

然后点击旁边的运行,下方如果出现OK,就表示创建成功

 然后在表那边点击刷新就可以看见新创建的表了

三、安装并配置MySQL模块

在api接口项目中,需要安装并配置MySQL这个第三方模块,俩连接和操作MySQL数据库

1.安装MySQL模块

在根目录的终端中运行如下命令:

npm i mysql@2.18.1

2.连接数据库

在项目中的根目录下新建db文件夹,并在其下方新建index.js文件,在此文件中创建数据库的连接对象

//导入MySQL模块
const mysql = require('mysql')
//创建数据库连接对象
const db = mysql.createPool({host: '127.0.0.1',       // 数据库主机名user: '你的数据库用户名',            // 数据库用户名password: '你的数据库密码', // 数据库密码database: 'test_mysql' // 数据库名
})
// 导出连接池
module.exports = db;

四、登录、注册

1.注册

1.1检测表单数据是否合法

在router_handle/user.js这个文件中判断用户名和密码是否为空,具体代码如下:

//注册用户的处理函数
exports.regUser = (req, res) => {//获取客户端提交到服务器的用户信息const userInfo = req.body// console.log(userInfo, '这个信息是:')//对表单中的数据,进行合法的校验if (!userInfo.username || !userInfo.password) {return res.send({code: 404,message: 'fail',detail:'用户名或密码不合法'})}
}

1.2检测用户名是否被占用

(1)导入数据库操作模块

在router_handle/user.js这个文件中导入数据库模块

//导入数据库
const db = require('../db/index')

(2)定义SQL语句

//定义SQL语句,查询用户名是否被占用const sqlStr='select * from ev_users where username=?'

(3)执行SQL语句并根据结果判断用户名是否被占用

db.query(sqlStr, userInfo.username, (err, results) => {//执行SQL语句失败if (err) {return res.send({code:500,message: 'fail',detail:err.message})}//判断用户名是否被占用if (results.length > 0) {return res.send({code:409,message: 'fail',detail:'用户名被占用,请更换其他用户名'})}})

1.3对密码进行加密处理

为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密存储。

在当前所做项目中,使用bcryptjs对用户密码进行加密,优点在于:

  • 加密之后的密码,无法被逆向破解;
  • 同一明文密码多次加密,得到的加密结果各不相同,保证了安全性。

(1)安装bcryptjs

在项目终端运行如下命令:

npm i bcryptjs@2.4.3

 (2)导入bcryptjs

在router_handle/user.js这个文件中导入bcryptjs

//导入bcryptjs
const bcrypt=require('bcryptjs')

(3)使用bcryptjs

在注册用户的处理函数中,确认用户名可用之后,调用bcrypt.hashSync(明文密码,随机盐的长度)方法,对用户的密码进行加密处理:

//调用bcryptjs-hashSync()对密码进行加密console.log(userInfo,'加密前:')userInfo.password = bcrypt.hashSync(userInfo.password, 10)console.log(userInfo,'加密后:')

1.4插入新用户

(1)定义插入用户的SQL语句

 //定义插入新用户的SQL语句const sql='insert into ev_users set ?'

(2)调用db.query()执行SQL语句,插入新用户

//调用db.query()执行SQL语句db.query(sql, { username: userInfo.username, password: userInfo.password }, (err, results) => {//判断SQL语句是否执行成功if (err) {return res.send({code:500,message: 'fail',detail:err.message})}//判断影响函数是否为1if (results.affectedRows != 1) {return res.send({code: 500,message: 'fail',detail:'注册用户失败,请稍后再试'})} else {return res.send({code: 200,message: 'success',detail:'注册成功'})}})

1.5优化res.send()代码

在以上的处理函数中我们可以发现,需要多次调用到res.send()向客户端响应处理失败的结果,为了简化以上代码,这里重新封装了一个res.trans()函数。

在app.js中,所有路由之前,声明一个全局中间件,为res对象挂载一个res.trans()函数

//封装res.trans
app.use((req, res, next) => {//code默认值为500,表示失败的情况//err可能是一个错误对象,也可能是一个错误的描述字符串res.trans = (err,code=500) => {res.send({code,message: 'fail',detail:err instanceof Error?err.detail:err})}next()
})

在router_handle/user.js这个文件中有关注册这个处理函数进行重新修改

//注册用户的处理函数
exports.regUser = (req, res) => {//获取客户端提交到服务器的用户信息const userInfo = req.body// console.log(userInfo, '这个信息是:')//对表单中的数据,进行合法的校验if (!userInfo.username || !userInfo.password) {// return res.send({//     code: 400,//     message: 'fail',//     detail: '用户名或密码不合法'// })return res.trans('用户名或密码不合法',400)}// } else {//     return res.send({//         code: 200,//         message: 'success',//         detail:''//     })// }//定义SQL语句,查询用户名是否被占用const sqlStr='select * from ev_users where username=?'db.query(sqlStr, userInfo.username, (err, results) => {//执行SQL语句失败if (err) {//原--未封装// return res.send({//     code:500,//     message: 'fail',//     detail:err.message// })//现--分装后return res.trans(err)}//判断用户名是否被占用if (results.length > 0) {//原--未封装// return res.send({//     code:409,//     message: 'fail',//     detail:'用户名被占用,请更换其他用户名'// })//现--分装后return res.trans('用户名被占用,请更换其他用户名',409)}//调用bcryptjs-hashSync()对密码进行加密// console.log(userInfo,'加密前:')userInfo.password = bcrypt.hashSync(userInfo.password, 10)// console.log(userInfo, '加密后:')//定义插入新用户的SQL语句const sql = 'insert into ev_users set ?'//调用db.query()执行SQL语句db.query(sql, { username: userInfo.username, password: userInfo.password }, (err, results) => {//判断SQL语句是否执行成功if (err) {// return res.send({//     code:500,// message: 'fail',// detail:err.message// })//现--分装后return res.trans(err)}//判断影响函数是否为1if (results.affectedRows != 1) {// return res.send({//     code: 500,//     message: 'fail',//     detail:'注册用户失败,请稍后再试'// })//现--分装后return res.trans('注册用户失败,请稍后再试')} else {return res.send({code: 200,message: 'success',detail:'注册成功'})}})})
}

1.6优化表单数据验证

表单验证的原则:前端验证为辅,后端验证为主,后端不能相信前端提交过来的任何内容。在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。
单纯的使用 if...e1se...的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性,让后端程序员把更多的精力放在核心业务逻辑的处理上。

(1)安装joi包,为表单中携带的每个数据项,定义验证规则

npm i joi

(2)安装@escook/express-joi中间件,来实现自动对表单数据进行验证的功能

npm i @escook/express-joi

(3)新建/schema/user.js用户信息验证规则模块,并初始化代码

//导入joi
const joi = require('joi')
/*** string 值必须是字符串* alphanum 值只能是包含a-zA-Z0-9的字符串* min(length)最小长度* max(length)最大长度* required() 值是必填项,不能为undefined* pattern(正则表达式) 值必须符合正则表达式的规则*/
//定义用户名和密码的验证
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)修改router/user.js中的代码

const express = require('express')
//创建路由对象
const router = express.Router()//打入用户路由处理函数模块
const userHandler = require('../router_handler/user')//导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
//导入需要的验证规则对象
const {reg_login_schema} = require('../schema/user')//注册新用户--新
//1.在注册新用户的路由中,声明局部中间件,对当前请求中携带的数据进行验证
//2.数据验证通过后,会把这次请求流转给后面的路由处理函数
//3.数据验证失败后,终止后端代码的执行,并抛出一个全局的error错误,进入全局错误级别中间件中进行处理
router.post('/register',expressJoi(reg_login_schema),userHandler.regUser )//登录---新
router.post('/login',userHandler.login)//导出路由对象
module.exports = router

(5)在app.js的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端

//导入joi
const joi = require('joi')
//中间部分的省略//定义错误级别的中间件
app.use((err, req, res) => {//验证失败导致的错误if (err instanceof joi.ValidationError) {return res.trans(err)}//未知的错误res.trans(err)})

(6)在router_handler/user.js文件中,注册用户处理函数这一块注释掉下面的代码

// if (!userInfo.username || !userInfo.password) {//     return res.trans('用户名或密码不合法',400)// }

2.登录

2.1检测登录表单的数据是否合法

将router/user.js中登录的路由代码修改如下

//登录---新
router.post('/login',expressJoi(reg_login_schema),userHandler.login)

2.2根据用户名查询用户的数据

(1)接收表单数据

//获取客户端提交到服务器的用户信息const userInfo = req.body

(2)定义SQL语句

//定义SQL语句,查询用户名是否被占用const sqlStr='select * from ev_users where username=?'

(3)执行SQL语句,查询用户的数据

db.query(sqlStr, userInfo.username, (res, results) => {//执行SQL语句失败if (err) {return res.trans(err)}//执行SQL语句成功,但是查询到数据条数不等于1if (results.length != 1) {return res.trans('登录失败')}})

2.3判断用户输入的密码是否正确

核心:调用bcrypt.compareSync(用户提交的密码,数据库中的密码)方法比较密码是否一致。

 db.query(sqlStr, userInfo.username, (err, results) => {//执行SQL语句失败if (err) {return res.trans(err)}//执行SQL语句成功,但是查询到数据条数不等于1if (results.length != 1) {return res.trans('登录失败',405)}//用户输入的密码和数据库中存储的密码进行对比const compareResult = bcrypt.compareSync(userInfo.password, results[0].password)console.log('这是:',compareResult,results[0].password,userInfo.password)//如果对比的结果等于false,则证明用户输入的密码错误if (!compareResult) {return res.trans('登录失败',400)} res.send('ok')})

2.4生成jwt的token字符串

注意:在生成token字符串的时候,一定要提出密码和头像的值

(1)通过es6的高级语法,快速剔除密码和头像的值,该代码书写于上述对比输入密码语句之后

//在服务器端生成token字符串const user = { ...results[0] ,password:'',user_pic:''}console.log(user,'这是:')

(2)安装生成token字符串的包

npm i jsonwebtoken

(3)在router_handler/user.js文件的头部区域,导入jsonwebtoken包

//导入生成的token包
const jwt=require('jsonwebtoken')

(4)创建config.js文件,并向外共享加密和还原token的jwtSecretKey字符串

module.exports = {//加密和解密的token密钥jwtSecretKey: 'test',//token有效期expiresIn:'10h'
}

(5)将用户信息对象加密成token字符串

//导入密钥配置文件
const config=require('../config.js')//对用户的信息进行加密,生成token字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })
console.log('这是:', tokenStr)

(6)将生成的token字符串响应给客户端

 //调用res.send()将token响应给客户端res.send({code: 200,message: 'success',token: 'Bearer '+tokenStr,detail:'登
})

2.5配置解析token的中间件

(1)安装解析token的中间件

npm i express-jwt@5.3.3

(2)在app.js中注册路由之前,配置解析token的中间件

//一定要在路由之前配置解析token的中间件
const { expressJwt } = require('express-jwt')
const config = require('./config')
app.use(expressJwt({ secret: config.jwtSecretKey}).unless({path:[/^\/api/]}))

(3)在app.js中的错误级别中间件中,铺货并处理token认证失败后的错误

//定义错误级别的中间件---暂不使用
app.use((err, req, res,next) => {//验证失败导致的错误// if (err instanceof joi.ValidationError) return res.trans(err)if(err.name==='UnauthorizedError') return res.trans('身份认证失败',401)// //未知的错误res.trans(err)    
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/72022.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【小白数学】为什么可以用拉格朗日乘子法求函数的极值【二】

我们在上一篇【小白数学】- 为什么可以用拉格朗日乘子法求函数的极值【一】已经介绍了一种较为“严谨“的方法来说明为什么拉格朗日乘子法可以帮助我们求具有等式约束条件下的函数的极值。虽然在我们的例子中”等式约束“中只有一个等式。但其实很容易推广到多个等式约束的情况…

JAVA面试_进阶部分_netty面试题

1.BIO、NIO 和 AIO 的区别? BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。 伪异步 IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。 NIO&#x…

考研出分24小时,人类精神状态图鉴

2月24日,上午10点起,各省考研初试成绩陆续公布,考生们或紧张的输入准考证号,或抱团等待“审判”。然而更魔幻的还在后头——下午4点,教育部竟在同一天直接发布了《2025年研考国家分数线》。 不少网友表示:…

川翔云电脑优势总结

在数字化时代,川翔云电脑依托云计算技术,为用户解决硬件性能瓶颈问题。川翔云电脑使用云渲码:【2355】 卓越硬件配置:配备 RTX 3090、48G 显存的 RTX 4090plus,支持 1 - 8 卡机配置,多卡并行计算能力强&am…

DeepSeek开源周Day4:三连发!突破 AI 训练瓶颈的立体解决方案,并行计算三剑客DualPipe、EPLB与Profile-data

项目地址: https://github.com/deepseek-ai/DualPipehttps://github.com/deepseek-ai/eplbhttps://github.com/deepseek-ai/profile-data 开源日历:2025-02-24起 每日9AM(北京时间)更新,持续五天 (4/5)! ​ ​ 一、背景概述 …

基于W2605C语音识别合成芯片的智能语音交互闹钟方案-AI对话享受智能生活

随着科技的飞速发展,智能家居产品正逐步渗透到我们的日常生活中,其中智能闹钟作为时间管理的得力助手,也在不断进化。基于W2605C语音识别与语音合成芯片的智能语音交互闹钟,凭借其强大的联网能力、自动校时功能、实时天气获取、以…

Vite与Turbopack现代构建工具架构解析:秒级构建的性能奥秘

引言:传统构建工具的效能瓶颈 Shopify将前端仓库迁移至Vite后,HMR更新时间从Webpack的4.2秒缩短至48毫秒。Turbopack在Vercel生产环境测试中,增量构建速度较Webpack快700%。ChromeOS团队采用Vite后,生产构建从Webpack的17分钟优化…

网络基础知识-2

N个节点完全互联的网型网即N个节点的无向完全图,无向完全图的边数计算如下:每个节点都要指向其他N-1个节点,但是因为无向两个节点之间的边会重复,因此有N(N-1)/2条边HDLC(高级数据链路控制协议)是一种面向比…

视频级虚拟试衣技术在淘宝的产品化实践

作为一种新的商品表现形态,内容几乎存在于手淘用户动线全流程,例如信息流种草内容、搜索消费决策内容、详情页种草内容等。通过低成本、高时效的AIGC内容生成能力,能够从供给端缓解内容生产成本高的问题,通过源源不断的低成本供给…

蓝桥备赛(三)- 条件判断与循环(下)

一、for循环 1.1 for 循环语法形式 for 循环是三种循环中使用最多的 , for 循环的语法形式如下: 1.2 执行流程 for 循环中 , 表达式1(初始化)只执行一次 ! 1.3 实践 练习:使用 for 循环在屏幕…

VMware Fusion 虚拟机Mac版 安装CentOS 7 系统

介绍 CentOS是Community Enterprise Operating System的缩写,也叫做社区企业操作系统。是企业Linux发行版领头羊Red Hat Enterprise Linux的再编译版本(是一个再发行版本),而且在RHEL的基础上修正了不少已知的 Bug ,相…

如果更换ip地址会怎么样?网络ip地址怎么更换

IP地址,作为网络设备的数字身份证,其稳定性和安全性对于网络通讯至关重要。然而,在某些特定情况下,我们可能需要更换设备的IP地址,以满足安全、隐私或网络管理的需求。那么,如果更换IP地址会怎么样&#xf…

网络通信/IP网络划分/子网掩码的概念和使用

文章目录 概述子网的考题子网掩码的历史有/无类地址子网划分!子网掩码超网技术/CIDR子网掩码和路由IP子网掩码定义 网络规划网络规划-拆子网网络规划-组超网子网划分案例 区分于其他特殊IP地址IP地址和网络地址子网掩码和网络地址子网掩码和广播地址 子网间的通信其他 概述 本…

评估自动驾驶(AD)策略性能的关键指标

以下是针对自动驾驶(AD)策略性能评测指标的详细解读,结合其物理意义与工程价值: 核心评测指标分类与含义 1. 安全性指标(Safety) 动态碰撞率(Dynamic Collision Ratio, DCR) 定义&a…

C++11相较于C++98的新特性介绍:列表初始化,右值引用与移动语义

一,列表初始化 1.1C98中传统的{} C98中一般数组和结构体可以使用{}进行初始化: struct Date {int _year;int _month;int _day; };int main() {int a[] { 1,2,3,4,5 };Date _date { 2025,2,27 };return 0; } 1.2C11中的{} C11以后想统一初始化方式&…

序列化是什么?常见的序列化方式有哪些?什么时候我们会用到序列化?

序列化(Serialization)是指将对象的状态信息转换为可以存储或传输的形式(如字节序列、XML 文档、JSON 字符串等)的过程。反序列化则是序列化的逆过程,它将存储或接收到的字节序列、XML 文档、JSON 字符串等转换回对象的…

Python解决“比赛配对”问题

Python解决“比赛配对”问题 问题描述测试样例解决思路代码 问题描述 小R正在组织一个比赛,比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制: 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,…

uniapp中使用leaferui使用Canvas绘制复杂异形表格的实现方法

需求: 如下图,要实现左图的样式,先实现框架,文字到时候 往里填就行了,原来的解决方案是想用css,html来实现,发现实现起来蛮麻烦的。我也没找到合适的实现方法,最后换使用canvas来实现&#xff…

大模型与呼叫中心融合:未来发展的潜力何在?

大模型与呼叫中心的结合,为企业带来了前所未有的发展机遇。通过提升服务效率、优化营销效果、降低运营成本、增强数据管理与分析能力、提升客户体验以及推动行业创新与变革,大模型呼叫中心正在重塑客户服务与营销的未来。 大模型与呼叫中心的结合具有巨…

vue3+ts+uniapp+unibest 微信小程序(第二篇)—— 图文详解自定义背景图页面布局、普通页面布局、分页表单页面布局

文章目录 简介一、自定义背景图布局1.1 效果预览1.2 实现思路1.3 custom-page 组件全量代码1.4 页面使用 二、普通页面布局2.1 效果预览2.2 实现思路2.3 公共样式部分2.4 页面使用 三、分页表单页面布局3.1 效果预览3.2 实现思路3.3 页面代码 简介 开发工具:VsCode…