node快速搭建接口实现登录退出,增删改查功能供前端使用,结尾有完整代码

news2024/11/18 15:40:11

用node快速搭建接口

    • 环境
    • 实现功能
    • 具体实现步奏
      • 数据库设计
      • 用express创建一个服务器实例
      • 创建数据库连接池
      • 配置跨域请求和解析前端数据
      • 登录接口实现
      • 验证token的中间件
      • 退出接口
      • 获取用户信息接口
      • 增删改查功能
    • 完整代码

环境

node版本v17.0.0
所用到的依赖

"dependencies": {
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.0",
    "md5": "^2.3.0",
    "mysql": "^2.18.1"
  }

实现功能

登录,退出,获取用户信息,以及登录后对数据库的增删改查操作
完整代码在结尾

具体实现步奏

数据库设计

本案列一共用到了两个表
user表:主要包含用户名,密码和权限(permission)
book表:主要包含书籍名称,作者,出版社,价格等
表的具体内容如下
在这里插入图片描述

用express创建一个服务器实例

const express = require('express')
const app = express()
app.listen(3001,()=>{
    console.log('serve is running at http://127.0.0.1:3001')
})

创建数据库连接池

 let db = mysql.createConnection({
     host:'127.0.0.1',
     user:'root',
     password:'123456',
     database:'books',
     port:3306
 })

 db.connect((err)=>{
     if(err) throw err;
     console.log('连接成功')    
 })

 setInterval(function(){
     db.query('select 1')
 },5000);

这种写法是最基本的写法,我们还可以通过数据库连接池来实现

// 创建连接池
const pool = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'books',
    connectionLimit: 10 // 设置连接池最大连接数
  });
  
  // 查询函数
  function query(sql, callback) {
    // 从连接池中获取一个连接
    pool.getConnection((err, connection) => {
      if (err) {
        callback(err, null);
      } else {
        // 执行查询
        connection.query(sql, (err, results) => {
          // 释放连接
          connection.release();
          callback(err, results);
        });
      }
    });
  }
  
  // 定时任务
  setInterval(() => {
    const sql = 'SELECT 1';
    query(sql, (err, results) => {
      if (err) {
        console.error(err);
      } else {
        // console.log('查询结果:', results);
      }
    });
  }, 5000);

在上面的代码中两种写法我们都设置了一个定时任务,这段代码是为了保持数据库连接处于活动状态,每隔5秒会向数据库发送一个请求,确保数据库连接不会因为长时间没有交互而被断开,这个技巧被称为“保活”

配置跨域请求和解析前端数据

以下配置表示允许所有的网址和方法请求

app.use((req, res, next) => {               //解决跨域问题,能够允许所有网址和方法的跨域处理
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
    if ('OPTIONS' === req.method) {
      res.sendStatus(200);
    } else {
      next();
    }
  });

配置解析前端传递的数据用到了body-parser这个包

const bodyParser = require('body-parser');
app.use(bodyParser.json())              //解析json
app.use(bodyParser.urlencoded({ extended: true }));             //解析客户端传递过来的参数 

登录接口实现

登录接口实现思路
首先接收前端请求的参数,从中解析出用户名和密码,先判断用户名和密码是否存在,不存在直接返回
如果用户名密码都存在,对密码进行md5加密,在进行数据库查询,没有找到返回用户名或密码错误
查询到数据说明,用户名密码正确生成token返回给客户端

// 登录接口
app.post('/login', (req, res) => {
    const { username, password } = req.body;

    if(!username || !password) res.json({ code:403,message: '用户名或密码不能为空' });
    // 进行MD5加密
    const md5Pwd = md5(password);
    // 查询数据库中是否存在该用户
    const sql = `SELECT * FROM user WHERE username='${username}' AND password='${md5Pwd}'`;
    query(sql, (err, result) => {
        if (err) throw err;
        if (result.length === 0) {
            res.json({
                code: 1,
                message: '用户名或密码错误'
            });
        } else {
            // 验证成功,生成token并返回
            const payload = {username: username}; // 按照需求设置payload
            const secretKey = '147258'; // 按照需求设置密钥
            const token = jwt.sign(payload, secretKey, {expiresIn: '1h'}); // 生成token,设置过期时间1小时

            // 将token返回给客户端
            res.json({
                token:token,
                code: 0,
                message: '登录成功'
            });
        }
    });
});

在这里插入图片描述

验证token的中间件

在所有路由之前定义一个中间件来验证token是否存在,以及token是否有效

const verifyToken = (req, res, next) => {
    // 获取请信息中的token
    const token = req.query.token;
    // 如果token不存在,则返回错误信息
    if (!token) {
      return res.json({ code:401,message: '未提供token' });
    }
    try {
      // 验证token是否有效
      const decoded = jwt.verify(token, '147258');
      // 将解码后的token信息保存到请求对象中
      req.user = decoded;
      next();
    } catch (err) {
      return res.json({ code:403,message: 'token验证失败' });
    }
  }

退出接口

退出接口需要在token验证有效后才可以请求,具体的操作就是删除token即可

app.post('/user/logout',verifyToken, (req, res) => {
    // 删除token
    res.clearCookie('token');
    res.json({
        code: 0,
        message: '退出成功'
    });
});

获取用户信息接口

从中间件中拿到解析出来的user对象,并拿到里面的username属性
根据该属性判断用户的角色,之后返回给前端,前端可以用来做权限判定

//获取用户信息接口
app.get('/user/getInfo',verifyToken,(req,res)=>{
    const username =req.user.username
    if(username==='admin'){
        res.json({
            code:200,
            roles:'admin',
            introduction: 'I am a super administrator',
            avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            name: '超级管理员'
        })
    }else if(username === 'editor'){
        res.json({
            code:200,
            roles:'editor',
            introduction: 'I am an editor',
            avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            name: '编辑'
        })
    }else{
        res.json({
            code:201,
            roles:'未找到改该角色'
        })
    }

})

增删改查功能

这些功能都需要登录后才可以操作,请求都需要携带token

// 查询
app.get('/books/getAll',verifyToken,(req, res) => {
    query('SELECT * FROM book', (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '获取数据成功',data:results})
    })
})

// 新增
app.post('/books/add',verifyToken,(req, res) => {
    const {title, author, publisher, publish_date, price} = req.body
    query(`INSERT INTO book (title, author, publisher, publish_date, price) VALUES ('${title}', '${author}', '${publisher}', '${publish_date}', '${price}')`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '新增成功', id: results.insertId})
    })
})

// 修改
app.put('/books/put/:id',verifyToken, (req, res) => {
    const {title, author, publisher, publish_date, price} = req.body
    query(`UPDATE book SET title='${title}', author='${author}', publisher='${publisher}', publish_date='${publish_date}', price='${price}' WHERE id=${req.params.id}`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '修改成功', id: req.params.id})
    })
})

// 删除
app.delete('/books/delete/:id',verifyToken, (req, res) => {
    query(`DELETE FROM book WHERE id=${req.params.id}`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '删除成功', id: req.params.id})
    })
})

这里token我直接就使用params方式传参,因为我测试环境的前端就是这样写的,也可以使用用header来传token,看自己的需求
在这里插入图片描述
到目前为止,一个完整的小案列就算完成了!

完整代码

以下是完整的代码,依赖需要自己安装,具体的版本前面有写过

const express = require('express')
const mysql = require('mysql')
const cors = require('cors')
const jwt = require('jsonwebtoken')
const md5 = require('md5')
const bodyParser = require('body-parser');



const app = express()

app.use(bodyParser.json())              //解析json
app.use(bodyParser.urlencoded({ extended: true }));             //解析客户端传递过来的参数 

app.use((req, res, next) => {               //解决跨域问题,能够允许所有网址和方法的跨域处理
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
    if ('OPTIONS' === req.method) {
      res.sendStatus(200);
    } else {
      next();
    }
  });
  


// let db = mysql.createConnection({
//     host:'127.0.0.1',
//     user:'root',
//     password:'123456',
//     database:'books',
//     port:3306
// })

// db.connect((err)=>{
//     if(err) throw err;
//     console.log('连接成功')    
// })

// setInterval(function(){
//     db.query('select 1')
// },5000);

// 创建连接池
const pool = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'books',
    connectionLimit: 10 // 设置连接池最大连接数
  });
  
  // 查询函数
  function query(sql, callback) {
    // 从连接池中获取一个连接
    pool.getConnection((err, connection) => {
      if (err) {
        callback(err, null);
      } else {
        // 执行查询
        connection.query(sql, (err, results) => {
          // 释放连接
          connection.release();
          callback(err, results);
        });
      }
    });
  }
  
  // 定时任务
  setInterval(() => {
    const sql = 'SELECT 1';
    query(sql, (err, results) => {
      if (err) {
        console.error(err);
      } else {
        // console.log('查询结果:', results);
      }
    });
  }, 5000);
//这段代码是为了保持数据库连接处于活动状态,
// 它会每隔5秒钟向数据库发送一个select 1的查询请求,确保连接不会因为长时间没有交互而被断开。
// 这个技巧被称为“保活”,可以让长时间运行的应用程序保持稳定的连接状态。

// 定义一个中间件来验证token,是否需要登录才可以操作
const verifyToken = (req, res, next) => {
    // 获取请信息中的token
    const token = req.query.token;
    // 如果token不存在,则返回错误信息
    if (!token) {
      return res.json({ code:401,message: '未提供token' });
    }
    try {
      // 验证token是否有效
      const decoded = jwt.verify(token, '147258');
      // 将解码后的token信息保存到请求对象中
      req.user = decoded;
      next();
    } catch (err) {
      return res.json({ code:403,message: 'token验证失败' });
    }
  }
  

// 登录接口
app.post('/login', (req, res) => {
    const { username, password } = req.body;

    if(!username || !password) return res.json({ code:403,message: '用户名或密码不能为空' });
    // 进行MD5加密
    const md5Pwd = md5(password);
    // 查询数据库中是否存在该用户
    const sql = `SELECT * FROM user WHERE username='${username}' AND password='${md5Pwd}'`;
    query(sql, (err, result) => {
        if (err) throw err;
        if (result.length === 0) {
            res.json({
                code: 1,
                message: '用户名或密码错误'
            });
        } else {
            // 验证成功,生成token并返回
            const payload = {username: username}; // 按照需求设置payload
            const secretKey = '147258'; // 按照需求设置密钥
            const token = jwt.sign(payload, secretKey, {expiresIn: '1h'}); // 生成token,设置过期时间1小时

            // 将token返回给客户端
            res.json({
                token:token,
                code: 0,
                message: '登录成功'
            });
        }
    });
});

//获取用户信息接口
app.get('/user/getInfo',verifyToken,(req,res)=>{
    const username =req.user.username
    if(username==='admin'){
        res.json({
            code:200,
            roles:'admin',
            introduction: 'I am a super administrator',
            avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            name: '超级管理员'
        })
    }else if(username === 'editor'){
        res.json({
            code:200,
            roles:'editor',
            introduction: 'I am an editor',
            avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            name: '编辑'
        })
    }else{
        res.json({
            code:201,
            roles:'未找到改该角色'
        })
    }

})

// 退出接口
app.post('/user/logout',verifyToken, (req, res) => {
    // 清除登录状态
    // 删除token
    res.clearCookie('token');
    res.json({
        code: 0,
        message: '退出成功'
    });
});


// 查询
app.get('/books/getAll',verifyToken,(req, res) => {
    query('SELECT * FROM book', (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '获取数据成功',data:results})
    })
})

// 新增
app.post('/books/add',verifyToken,(req, res) => {
    const {title, author, publisher, publish_date, price} = req.body
    query(`INSERT INTO book (title, author, publisher, publish_date, price) VALUES ('${title}', '${author}', '${publisher}', '${publish_date}', '${price}')`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '新增成功', id: results.insertId})
    })
})

// 修改
app.put('/books/put/:id',verifyToken, (req, res) => {
    const {title, author, publisher, publish_date, price} = req.body
    query(`UPDATE book SET title='${title}', author='${author}', publisher='${publisher}', publish_date='${publish_date}', price='${price}' WHERE id=${req.params.id}`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '修改成功', id: req.params.id})
    })
})

// 删除
app.delete('/books/delete/:id',verifyToken, (req, res) => {
    query(`DELETE FROM book WHERE id=${req.params.id}`, (err, results) => {
        if (err) throw err;
        res.json({code:0,message: '删除成功', id: req.params.id})
    })
})


app.listen(3001,()=>{
    console.log('serve is running at http://127.0.0.1:3001')
})

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

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

相关文章

量子力学奇妙之旅-从相对论下薛定谔方程到量子场论

专栏目录: 高质量文章导航-持续更新中 1.用于描述一个多粒子体系的波函数 前置:最小的尺度 由于不确定性原理 因此为了测量到更小的空间尺度,我们就需要提高探测粒子的动量

RSA非对称加密(附工具类Util)

文章目录 非对称加密是什么非对称加密通信流程RSA非对称加密算法非对称加密工具类Util及案例演示 之前写过一篇关于DES对称加密的帖子,感兴趣的小伙伴可以去看看:DES对称加密算法 今天主要聊聊什么是非对称加密,以及它是如何实现的。 一、非…

f(x)与|f(x)|,f ‘ (x),F(x)常见关系。

1.f(x)与|f(x)|关系。 1.连续关系。(f(x)在"[a,b]上连续" > |f(x)|在"[a,b]连续") ①如果f(x)在[a,b]上连续。则|f(x)|在[a,b]上连续. (因为f(x)在x0的连续点>x0必为|f(x)|的连续点) 注:”[a,b]连续“包括&#…

PUBG绝地求生的制作公司计划今年推出NFT元宇宙游戏

欢迎来到Hubbleverse 🌍 关注我们 关注宇宙新鲜事 📌 预计阅读时长:5分钟 本文仅代表作者个人观点,不代表平台意见,不构成投资建议。 近年来,元宇宙的概念在游戏界获得了极大的关注。元宇宙指的是一个虚…

武汉大学惯性导航课程合集【2021年秋】2.1 惯导机械编排算法

Vrb是客观存在的,b相对于r的速度(从R到b的变化),右上角的b表示投影到p坐标系,只是表达数值的不同。 (工程上5-10,50倍)奈奎斯特采样率,香农采样定理,又称奈…

Synthesys:语音合成和视频生成平台

【产品介绍】 Synthesys是一个基于人工智能的语音合成和视频生成平台,可以让你用几分钟的时间,就能制作出专业的音频和视频内容,无需花费大量的金钱和时间去雇佣演员、摄像机或音频设备。Synthesys的技术可以把你的文本转换成逼真的人声和人像…

《花雕学AI》AI时代来临,互联网教父凯文·凯利给你50条生存指南:5000天后的世界会是什么样?

你知道凯文凯利吗?他是《连线》杂志的创始人之一,被誉为“世界互联网教父”,他的预言和观点影响了无数人的思考和行动。他曾经预言过互联网、社交媒体、区块链等技术的发展和变革,而现在,他又给我们带来了一个全新的预…

Vue电商项目--防抖节流应用

演示卡顿现象 正常:事件触发非常频繁,而且每一次的触发,回调函数都要去执行(如果时间很短,而回调函数内部有计算,那么很容易出现浏览器卡顿) 正常情况下(用户慢慢的操作&#xff0…

基于Transformer的DETR的注意力权重可视化,非CAM可视化技术

神经网络的可视化可以客观的解释 “黑盒” ,所以一直以来都是论文中必不可少的工作。对于深度卷积神经网络,一般用CAM进行可视化研究。遗憾的是,基于Transformer的神经网络可视化,CAM并不奏效。所以,本文章提供一套基于…

考研机试刷题第二天:任意进制转任意进制【高进度短除法】

理一下思路&#xff1a; 看了y总的视频之后我觉得这道题其实只需要对上次写的进制转换微微做一下调整即可。 于是我写出了下面的代码 #include <iostream> #include <vector> #include <algorithm> #include <cstring>using namespace std;vector<…

SpringCloud微服务如何进行权限校验?

一、概述&#xff1a; 1、什么是Spring Cloud&#xff1f; SpringCloud, 基于SpringBoot提供了一套微服务解决方案&#xff0c;包括服务注册与发现&#xff0c;配置中心&#xff0c;全链路监控&#xff0c;服务网关&#xff0c;负载均衡&#xff0c;熔断器等组件&#xff0c;…

数据结构(六)—— 二叉树(4)回溯

文章目录 一、题1 257 二叉树的所有路径1.1 写法11.2 写法2 一、题 1 257 二叉树的所有路径 1.1 写法1 递归回溯&#xff1a;回溯是递归的副产品&#xff0c;只要有递归就会有回溯 首先考虑深度优先搜索&#xff1b;而题目要求从根节点到叶子的路径&#xff0c;所以需要前序…

第一章 算法概述

第1章-算法概述 总分&#xff1a;100分 得分&#xff1a;30.0分 1 . 填空题 简单 10分 递归算法必须具备的两个条件是___和___ 回答错误 答案 边界条件或停止条件、递推方程或递归方程 2 . 填空题 中等 10分 冒泡排序时间复杂度是___&#xff0c;堆排序时…

深度学习笔记--本地部署Mini-GPT4

目录 1--前言 2--配置环境依赖 3--下载权重 4--生成 Vicuna 权重 5--测试 6--可能出现的问题 1--前言 本机环境&#xff1a; System: Ubuntu 18.04 GPU: Tesla V100 (32G) CUDA: 10.0 项目地址&#xff1a;https://github.com/Vision-CAIR/MiniGPT-4 2--配置环境依赖 …

18.考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化

说明书 MATLAB代码&#xff1a;考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化 关键词&#xff1a;碳交易 电制氢 阶梯式碳交易 综合能源系统 热电优化 参考文档&#xff1a;《考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化》基本复现 仿真平台&#xff1a;M…

洞车常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交&#xff0c;提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

表情符号(emoji)大全,只此一文便够了

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 表情符号&#xff08;emoji&#xff09;大全、只此一文便够了 摘要集中展示笑脸和动物人庆贺和物品食品和物交通和地点符号 符号表smileys_and_peopleanimals_and_naturefood_and_dr…

Spring Boot集成ShardingSphere实现数据分片(三) | Spring Cloud 42

一、前言 在前面我们通过以下章节对数据分片有了基础的了解&#xff1a; Spring Boot集成ShardingSphere实现数据分片&#xff08;一&#xff09; | Spring Cloud 40 Spring Boot集成ShardingSphere实现数据分片&#xff08;二&#xff09; | Spring Cloud 41 知道数据分片…

Sentinel --- 简介、流量控制

一、Sentinel 1.1、雪崩问题及解决方案 雪崩问题 微服务中&#xff0c;服务间调用关系错综复杂&#xff0c;一个微服务往往依赖于多个其它微服务。 如图&#xff0c;如果服务提供者I发生了故障&#xff0c;当前的应用的部分业务因为依赖于服务I&#xff0c;因此也会被阻塞。此…

Spring AOP(重点、难点)

Spring AOP&#xff08;重点、难点&#xff09; 文章目录 Spring AOP&#xff08;重点、难点&#xff09;1.aop引入1.1 使用场景与概念引入1.2 以数据校验记录操作日志为例 写一组代码进行递推初始阶段 老老实实一个一个写&#xff1a;阶段一 **将日志和验证方法包装到一个类里…