图书商城项目练习②后端服务Node/Express/Sqlite
本系列文章是为学习Vue的项目练习笔记,尽量详细记录一下一个完整项目的开发过程。面向初学者,本人也是初学者,搬砖技术还不成熟。项目在技术上前端为主,包含一些后端代码,从基础的数据库(Sqlite)、到后端服务Node.js(Express),再到Web端的Vue,包含服务端、管理后台、商城网站、小程序/App,分为下面多个篇文档。
系列目录:
- 图书商城Vue+Element+Node+TS项目练习
- 图书商城①管理后台Vue2+ElementUI
- 图书商城②后端服务Node+Express+Sqlite
- 未完成:商城网站Vue3+TS、商城APP端Vue3+TS+uniapp
00、后端服务Node/Express/Sqlite
后端服务实现的比较简单,仅仅只是为了满足前端的最小需要。实现了登录、文件上传下载、一些数据的增删改查等基础功能,正式后端服务中的权限、安全、性能并没有考虑,主要是也不太熟悉。用Node做一个简单的后端服务还是比较容易的,前端开发还是有必要了解一下,不仅要和前端同行卷,更要往外卷,卷起来!—— 跟自己卷就行了!(不得不)持续学习,(被迫)不断进步!
技术路线:
- Node.js,开发运行环境
v16.17.1
- express,Web组件
v4.18
- sqlite3,数据库
相关组件:
express.static
:静态资源托管,express提供的,无需额外安装。multer
:文件上传
源代码地址:Github / KWebNote,Gitee / KWebNote,后台服务端代码在目录server下。
01、数据库Database
1.1、用什么数据库?Sqlite
Node支持多种数据,官网Database integration。本项目采用的是Sqlite数据库,Sqlite是一款轻量的关系型数据库,无需安装、无需配置、无需单独启动。SQLite是一种嵌入式数据库,它的数据库就是一个db库文件,极度轻量,该有的功能都有。可嵌入到多种语言中使用,当然也支持JS,引入Sqlite的JS库“sqlite3
”即可进行数据库增删改查操作了。
️项目中的Sqlite数据库文件就一个db文件“book_db.db ”单文件20KB。
GUI管理工具推荐下载 SQLiteStudio,非常好用,免费开源,功能齐全,支持Windows、IOS、Linux。
1.2、数据库结构设计
基于项目需求,设计了5个表,表结构相对比较简单,设计比较也比较随意。
️书籍信息表“books”
编码 | 名称 | 描述/备注 |
---|---|---|
id | 编号ID | 主键,自增长 |
name | 书籍名称 | |
catgory | 图书分类编号 | 来自字典数据,字典类型dictype.code =bookType ,关联字典数据dicdata.code |
tag | 促销标签 | 来自字典数据,字典类型dictype.code =bookTag ,关联字典数据dicdata.code |
author | 作者 | |
price | 价格 | |
imgs | 图片 | 图片集合字符串,逗号分割,最多8张 |
comments | 评论数 | 评论内容就模拟随机产生了 |
introduction | 详情介绍 | 富文本内容,长度最大8000,支持上传图片 |
status | 状态 | 枚举值:正常、下架 |
createtime | 创建时间 | 时间戳 |
lasttime | 修改时间 | 时间戳 |
️订单表“order”
编码 | 名称 | 描述/备注 |
---|---|---|
id | 编号ID | 主键,自增长 |
uid | 用户id | 下单的用户 |
money | 订单金额 | |
products | 商品 | 购买的图书商品列表,书名、数量、价格的Json字符串 |
status | 状态 | 枚举值:unpay(未支付)、canceled(已取消)、done(已完成) |
createtime | 创建时间 | 时间戳 |
INSERT INTO [order] (createtime,status,products,money,uid)
VALUES (1675319639624,'unpay','[{"name":"论语","price":155,"total":1},{"name":"论语2","price":15,"total":2}]',113,1);
数据:
id | uid | money | products | status | createtime |
---|---|---|---|---|---|
1 | 1 | 113 | [{"name":"论语","price":155,"total":1},{"name":"论语2","price":15,"total":2}] | unpay | 1675319639624 |
2 | 1 | 1334 | [{"name":"论语","price":155,"total":1},{"name":"论语2","price":15,"total":2}] | canceled | 1675319639624 |
3 | 2 | 333 | [{"name":"论语","price":155,"total":1},{"name":"论语2","price":15,"total":2}] | done | 1675319639624 |
️用户表“user_info”
编码 | 名称 | 描述/备注 |
---|---|---|
id | 编号ID | 主键,自增长 |
name | 用户名 | |
pwd | 密码 | 明文存储,没有加密。实际项目中不会这么干 |
数据:
id | pwd | name |
---|---|---|
1 | 123 | admin |
2 | 123456 | ading |
3 | test | test |
️字典类型“dictype”
编码 | 名称 | 描述/备注 |
---|---|---|
id | ID | 主键,自增长 |
code | 类型编码 | 不可重复,如booktype (图书分类) |
name | 名称 | |
tree | 是否树形结构 | 1=树形结构,0=非树形结构,主要是区分数据的结构 |
数据:
id | code | name | tree |
---|---|---|---|
1 | bookTag | 商品标签 | 0 |
2 | bookType | 书籍分类 | 1 |
️字典数据“dicdata”
编码 | 名称 | 描述/备注 |
---|---|---|
id | 编号ID | 主键,自增长 |
name | 名称 | |
code | 类型code | 同dictype.code ,用于区分不同字典数据 |
pid | 父ID | 一级(根)的值为0,以此来判等根节点并组装为树 |
数据:
id | name | code | sort | pid |
---|---|---|---|---|
1 | 促销 | bookTag | 1 | 0 |
2 | 热卖 | bookTag | 2 | 0 |
3 | 新上市 | bookTag | 3 | 0 |
4 | 买它! | bookTag | 4 | 0 |
5 | 科技 | bookType | 1 | 0 |
6 | 计算机/网络 | bookType | 1 | 5 |
7 | 人文历史 | bookType | 2 | 0 |
8 | 医学12333 | bookType | 2 | 5 |
25 | 小说 | bookType | 1 | 7 |
26 | 历史 | bookType | 2 | 7 |
02、创建项目
创建一个“server”目录,就创建完毕了。后端服务做的比较简陋,文件结构也比较清晰。
api
目录:服务端的api接口,使用Express的路由来构建。base.js
:基础公共api,如登录、首页数据book.js
:书籍管理apifile.js
:文件上传apiorder.js
:订单模块apisystem.js
:系统管理模块api,如字典管理
db
目录:book_db.db
:sqlite数据库文件,就一个db文件,非常轻量。db.js
:sqlite数据库访问的封装。
file
目录:上传的文件存储在这里的。index.js
:入口JS文件,组装api目录下的API接口、注册HTTP服务。
服务端运行也同样简单,用node启动入口JS文件index.js
即可。
node index.js
03、sqlite3数据库api
安装sqlite3
的Node组件:cnpm install sqlite3 -S
,在server
目录下运行。
数据库的操作接口很简单,指定sqlite的db文件,申明一个sqlite3实例对象,就可干活了。主要就是执行sql(新增insert、修改update、删除delete)、查询(select)数据!
let sqlite3 = require('sqlite3').verbose();
let db = new sqlite3.Database('file-name.db'); //指定sqlite的db文件
//查询,参数row为查询的结果结合
db.all("select * from 表名",function(err,row){
console.log(row);
})
//执行sql:新增、修改、删除
const sql = "delete from dicdata where id=?";
const params = [req.body.id];
db.run(sql, params, function (error) { })
可以做一个简单的封装,统一返回的数据结构。
db.queryData
:查询数据db.executeSql
:执行sql:增、删、修改
//加载sqlite并创建数据库实例
let sqlite3 = require('sqlite3').verbose();
let db = new sqlite3.Database('./db/book_db.db');
// 响应数据结构
function ResponsData(error) {
this.status = error ? 'Error' : 'OK';
this.message = error ? error : null;
}
//异常处理
db.printError = function (error) {
if (error) console.error(error);
}
//查询数据
db.queryData = function (sql, params, callback) {
if (!params) params = {};
db.all(sql, params, function (error, rows) {
db.printError(error);
let resData = new ResponsData(error);
callback(resData, rows);
})
}
//执行sql:增、删、修改
db.executeSql = function (sql, params, callback) {
db.run(sql, params, function (error) {
db.printError(error);
let resData = new ResponsData(error);
callback(resData);
})
}
module.exports = db;
参数化:使用参数化单独管理sql中的变量参数,更安全,这是防止SQL注入的基础手段。
- 使用问号“?”作为参数的占位符,参数按照顺序组装为数组。
insert into books values(NULL,?,?,?,?,?,?,?,?,?,?,?)
- 模糊查询时,在参数中构造模糊查询表达式:
wheresql = " where name like ?"; params.push("%" + req.body.name + "%");
更多SQL使用详见Github / KWebNote。
04、Express开发后台API
4.1、Express入门
express 是Node.js 的组件_(express /ɪkˈspres/ 快速、快递)_,一个快速、开放、极简的 Web 开发框架, 提供一系列强大特性帮助你创建各种Web应用。使用非常简便、灵活,用于搭建WEB服务,所以我们就用Express来搭建后端API服务。
- 1、安装express:
cnpm install express -S
,在server
目录下运行。 - 2、创建实例,添加接口、监听端口。
- 3、运行,在命令行中用node命令运行该JS文件,启动Express服务:
node server.js
。
//加载组件
let express = require('express');
//创建一个服务端服务实例server
let server = new express();
//定义一个get接口
server.get('/hello', (req, res) => {
res.send('Hello World!')
})
//定义一个post接口
server.get('/send', (req, res) => {
console.log(req.body); //post发送的数据
})
//***** 配置监听端口 *****/
server.listen(801, err => {
if (!err)
console.log('服务器启动成功,地址:http://localhost:801')
})
启动:node server.js
测试:http://localhost:801/hello
4.2、Express路由/API接口
后端的API接口的封装比较简单了,监听路由API地址,接收请求、处理(操作数据库),然后返回响应结果。为了更好管理后端的API,使用Express的路由express.Router()
来组织不同模块的API接口。各个模块的API可以单独管理,然后统一组装。
比如book.js
,图书管理api:
//引入express
let express = require('express');
//获取Express的路由
let router = express.Router();
//引入db库
const db = require('../db/db.js');
//设置路由:获取单个book数据,get方式
router.get('/book/id', (req, res) => {
//sql语句
let sql = "select id,name,author,introduction,imgs,status,catgory,price,tag,comments,createtime,lasttime from books where id =?";
//调用db封装的查询api,返回数据
db.queryData(sql, [req.query.id], (resData, rows) => {
resData.data = rows[0];
res.send(resData);
});
})
//设置路由:新增、修改,post方式
router.post('/book/save', (req, res) => {
let sql = '';
// 参数,在sql中用?占位(按照顺序)
let params = [req.body.name, req.body.author, req.body.introduction, req.body.imgs, req.body.status, req.body.catgory, req.body.price, req.body.tag, req.body.comments, req.body.createtime ?? Date.now(), Date.now()];
//update 修改数据
if (req.body.id) {
sql = "update books set name=?,author=?,introduction=?,imgs=?,status=?,catgory=?,price=?,tag=?,comments=?,createtime=?,lasttime=? where id=?";
params.push(req.body.id);
}
else //insert 新增数据 (name,author,introduction,img,status)
sql = "insert into books values(NULL,?,?,?,?,?,?,?,?,?,?,?)";
db.executeSql(sql, params, resData => {
if (resData.status == 'OK')
resData.message = '保存成功';
res.send(resData);
});
})
// 导出路由router
module.exports = router;
在入口文件index.js
中引用安装book.js
的路由。
//加载组件
let express = require('express');
//创建一个服务端服务实例server
let server = new express();
// 书籍模块api
const book = require('./api/book.js');
const path='/api';
server.use(path, book);
完整API接口详见Github / KWebNote。
4.3、文件的上传
常用的图片、Excel等附件的上传,文件本质上也是数据,同其他API接口一样,通过POST方式提交文件数据。HTML的FORM表单提交支持多种类型,其中multipart/form-data
混合类型就是专用于提交二进制文件的。
HTML中
<form>
表单enctype
:编码类型(encode type),规定了form表单在发送到服务器时候编码方式。
- application/x-www-form-urlencoded:编码所有字符(默认),传输字符内容。
- multipart/form-data :混合类型, 表单中有文件上传时使用。
- text/plain:纯文体,空格转换为 “+” 加号,不对特殊字符编码。
在Express中上传文件,使用中间件multer
,是专门用来处理复合表单multipart/form-data
数据,用来处理后端的文件上传。
- 安装
multer
插件:cnpm i -S multer
。,在server
目录下运行。 - 配置文件上传接口“/upload”:
let express = require('express');
let router = express.Router();
let multer = require('multer');
// 申明multer实例,并配置文件保存路径、文件名
let upload = multer({
storage: multer.diskStorage({
//文件存储位置
destination: function (req, file, callback) {
callback(null, './file/')
},
//文件命名
filename: function (req, file, callback) {
callback(null, Date.now() + '-' + file.originalname);
}
})
});
// 配置文件上传接口
router.post('/upload', upload.single('file'), function (req, res, next) {
if (!req.file) {
res.json({ status: 'Error', message: '文件上传错误:文件不存在' });
return;
}
let file = req.file;
//返回响应消息:文件的相对URL地址
res.json({
status: 'OK',
name: file.filename,
url: '/file/' + file.filename,
});
});
测试一下,注意字段名称应为“file”,与后端保持一致。
05、托管静态资源/部署网站
5.1、静态资源托管
前面上传了文件,那客户端如何访问静态文件呢?官方文档:利用 Express 托管静态文件。
在Node中使用express
自带的静态资源管理插件,即可托管静态文件,如图片、网页文件等。因此不仅可以实现文件下载,还支持部署静态网站。
// 使用内置的“express.static”实现静态文件代理,参数为资源地址。
//图片静态资源,第一个参数为url路由,“./file”为存放文件的文件夹
server.use('/file', express.static('./file'));
这就完了?是的,很简单!访问一下试试:
http://localhost:3000/file/f1.jpg
http://localhost:3000/file/1676274037570-gif007.gif
5.2、部署网站/前端路由重定向
前面通过express.static
可以托管静态资源,也就意味着可以部署前端网站,可以把编译好的管理后台“book_admin”通过Node来部署试试。
//部署管理后台网站
server.use('/bookadmin', express.static('./book_admin'));
登录可用,页面访问正常!
刷新一下,糟了,出错了,无法访问此页面~
这是什么原因呢?
- Node后端只配置了路由地址“
/bookadmin
”,没有配置“/bookadmin/home
”的路由,后端就没人响应。 - “
/home
”是前端路由,前端路由vue-router
使用的是history
模式,如果用hash
模式就没问题。
怎么解决?
- 方法1:前端路由改用
hash
模式,/bookadmin#home
,hash值#home
后端不会管,前端处理。 - 方法2:后端处理,重定向到单页应用的页面即可。具体方式也有多个:
- 方式1:引用组件“connect-history-api-fallback”解决。
- 方式2:强制重定向到主页面
index.html
,代码如下:
//管理后台"book_admin"的部署
//静态资源
server.use('/bookadmin', express.static('./book_admin'));
const fs = require('fs')
const rpath = require('path')
//前端路由的重定向
server.get('/bookadmin/*', function(req, res) {
const html = fs.readFileSync(rpath.resolve(__dirname, '../server/book_admin/index.html'), 'utf-8')
res.send(html)
})
重启node服务解决
D:\Project_Files\kwongGit\KWebNote\server>node index.js
服务器启动成功,地址:http://localhost:3000
参考资料:
- Express 中文网
- NodeJS中使用SQLite3
- 还有一个更快的sqlite库:better-sqlite3
️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀
图书商城项目练习②后端服务Node/Express/Sqlite的更多相关文章
- Xbin-Store(分布式商城)项目所用Linux服务系列 FastDFS安装(五)
系列 Xbin-Store(分布式商城)项目所用Linux服务系列 MySQL安装(一) Xbin-Store(分布式商城)项目所用Linux服务系列 Redis集群安装(二) Xbin-Store( ...
- Javaweb学习笔记——(二十四)——————图书商城项目
图书商城 环境搭建 1.导入原型 *用户模块 *分类模块 *图书模块 ...
- 【Java EE 学习 25 上】【网上图书商城项目实战】
一.概述 1.使用的jdk版本:1.6 2.java EE版本:1.6 3.指导老师:传智播客 王建 二.小项目已经实现的功能 普通用户: 1.登陆 2.注册 3.购物 4.浏览 管理员用户(全部管理 ...
- 夺命雷公狗---node.js---20之项目的构建在node+express+mongo的博客项目5mongodb在项目中实现添加数据
我们上一步就引入了mongodb了,那么下一步就要开始写添加数据了,不过有个前提是先将表单的数据处理好: 最基本的这部现在已经成功了,因为最基本的这步就是先将表单处的提交方式和提交地址给处理好,这里和 ...
- Vue nodejs商城项目- 前后端数据传递
.利用Mongoose查询MongoDB 通过mongoose插件可以简捷地从mondodb中获取数据,首先安装mongoose: cnpm install mongoose --save 使用m ...
- 夺命雷公狗---node.js---21之项目的构建在node+express+mongo的博客项目6之数据的遍历
首先还是来链接数据库,然后就查找,如下所示: /** * Created by leigood on 2016/8/31. */ var express = require('express'); v ...
- 夺命雷公狗---node.js---19之项目的构建在node+express+mongo的博客项目4mongodb在项目中的基本引入
首先我们在命令行下先建立这个库: 然后我们在项目中引入mongodb的模块: var MongoClient = require('mongodb').MongoClient; var DB_STR ...
- 夺命雷公狗---node.js---18之项目的构建在node+express+mongo的博客项目3头尾左侧分离法
在实际的开发中我们的项目往往都是需要头尾分离开来的,居然是后台管理界面当然也不能错过这么好的这步.. 首先我们将我们要分离的部分代码先剪切出来,如下所示: 将他们都弄出来... 这部分的内容分别对应的 ...
- 夺命雷公狗---node.js---16之项目的构建在node+express+mongo的博客项目1
废话不多说我们直接开工... 直接在目录下打开黑窗口: 然后就开始看看我们创建出来的文件了: 然后就开始创建项目下的目录了: 从这里就可以清晰的看得到我们的目录都是以前后台来分离开来的,引入模版也很简 ...
- 网上图书商城项目学习笔记-012BOOK模块查询2
一.分析 > 按图名查询(模糊)(分页)> 按作者查询(分页)> 按出版社查询(分页)> 按id查询> 多条件组合查询(分页) 二.代码 1.view层 (1)gj.js ...
随机推荐
- 浏览器层面优化前端性能(1):Chrom组件与进程/线程模型分析
现阶段的浏览器运行在一个单用户,多合作,多任务的操作系统中.一个糟糕的网页同样可以让一个现代的浏览器崩溃.其原因可能是一个插件出现bug,最终的结果是整个浏览器以及其他正在运行的标签被销毁. 现代操作 ...
- Spring入门系列:浅析知识点
前言 讲解Spring之前,我们首先梳理下Spring有哪些知识点可以进行入手源码分析,比如: Spring IOC依赖注入 Spring AOP切面编程 Spring Bean的声明周期底层原理 S ...
- 【原型设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 原型模式(Prototype Pattern)是一种创建型设计模式,使你能够复制已有对象,而无需使代码依赖它们所属的类,同时又能保证性能. 这种模式是实现了一个原型接口,该接口用于创建当前对象的 ...
- 百度首页静态展示页面HTML+CSS
一直觉得百度首页很复杂的,有那么多的东西,跟这个博主学习了之后,仿写了一下,样式好像很简单 只设置的一些组件的高度而已,不得不说,CSS真是个好东西呀 话不多说,直接上代码 <!DOCTYPE ...
- 从 API 网关聊到 API 管理
在 API 管理中,通常会有这些痛点: 1.企业不清楚到底有多少个API,无法形成API资产管理等问题. 2.API在不同集群的生命周期问题. 3.API运行状态监控和告警问题. 4.API请求限流. ...
- ts中报错信息收集
1. 错误代码 参考:https://www.mmbyte.com/article/92849.html 1 state.localuserInfo = JSON.parse(localStorage ...
- 《HelloGitHub》第 85 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...
- Python-zmail发送简单邮件
简介: Zmail 使得在python3中发送和接受邮件变得更简单.你不需要手动添加服务器地址.端口以及适合的协议,zmail会帮你完成.此外,使用一个python字典来代表邮件内容也更符合直觉 安装 ...
- UIOTOS:一款无门槛的前端0代码搭建工具
什么是UIOTOS? UIOTOS中文名称前端大师,是一款基于图形技术的前端0代码工具,支持通过连线和嵌套无门槛来搭建各类复杂的的交互界面,包括后台管理系统.组态数据大屏等,实现跟代码开发媲美的效果. ...
- 2022-04-26:给定一个数组componets,长度为A, componets[i] = j,代表i类型的任务需要耗时j 给定一个二维数组orders,长度为M, orders[i][0]代表i
2022-04-26:给定一个数组componets,长度为A, componets[i] = j,代表i类型的任务需要耗时j 给定一个二维数组orders,长度为M, orders[i][0]代表i ...