代码结构的优化、提交信息校验、文件的上传
一、代码结构的优化
问题:由于现在开发写的代码全部都是在 app.js 文件,这样会导致单个文件非常的大,臃肿。所以在开发的时候,一般我们都是要按照一定的规范去切分我们的代码,形成一定的分层关系,便于后期的维护。
解决方案:
1.对app.js里面的代码需要做切分
2.根据业务的功能然后建立对应的文件或者文件夹进行管理
1.1数据库优化
创建文件夹db,对有个数据库建一个目录,根据数据库的类型,拆分为不同的数据库操作文件,这里使用的是mongoose所以创建mongodb.js(如果使用mysql就创建mysql.js这样)
第一步:
/*1. 根据数据库的类型,拆分为不同的数据库操作文件*/
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/dingli', {useNewUrlParser: true, useUnifiedTopology: true});
//定义模型
const WebsiteSchema = mongoose.Schema({
siteName: String,
keyword: String,
description: String,
copyright: String,
benanhao: String,
address: String,
phoneNumber: String,
tel: String,
email: String,
qrcode: String,
appcode: String,
});
//引入模型
const WebsiteModel = mongoose.model('Website', WebsiteSchema);
第二步:
对数据库表的处理,在开发的时候,一般我们会操作很多的表。那么我们需要在建立一个models目录,对所有的表的模型进行管理。
models/website.js
/*引入数据库*/
const mongoose = require('../db/mongodb.js');
/*2. 这个是代码对网站配置表的操作,在开发的时候,一般我们会操作很多的表。那么我们需要在建立一个目录,对所有的表的模型进行管理。*/
const WebsiteSchema = mongoose.Schema({
siteName: String,
keyword: String,
description: String,
copyright: String,
benanhao: String,
address: String,
phoneNumber: String,
tel: String,
email: String,
qrcode: String,
appcode: String,
});
const WebsiteModel = mongoose.model('Website', WebsiteSchema);
/* 等一下需要操作表的地方,需要使用的到模型,所以模型也要暴露 */
module.exports = WebsiteModel;
db/mongodb.js
/*1. 根据数据库的类型,拆分为不同的数据库操作文件*/
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/dingli', {useNewUrlParser: true, useUnifiedTopology: true});
/* 在模型文件夹下的各个模型需要使用的 mongoose ,需要导出 */
module.exports = mongoose;
在app.js里增加
/* 3. 由于业务里面要使用到模型,则我们需要导入模型 */
const WebsiteModel = require('./models/website.js');
第三步:
数据库配置,创建目录condig,db.js定义数据的连接地址
const mgDsn='mongodb://localhost:27017/dingli'
module.exports = {
mgDsn
}
db/mongodb.js修改为
/*1. 根据数据库的类型,拆分为不同的数据库操作文件*/
const mongoose = require('mongoose');
const dbConfig = require('../config/db.js');
mongoose.connect(dbConfig.mgDsn, {useNewUrlParser: true, useUnifiedTopology: true});
/* 在模型文件夹下的各个模型需要使用的 mongoose ,需要导出 */
module.exports = mongoose;
目录结构:
nodemon app.js启动
http://localhost:3000/contact
http://localhost:3000/admin/index
都没有问题 都能展示
1.2路由信息优化
通过 app.get 这种定义的urL地址,一般也叫做路由信息。
上网:路由器。在web开发里面里面也有路由的概念,web里面的路由指的是,我们定义好urL地址和业务代码之间的关系,我们称之为路由映射,或者叫做路由规则。
由于一个网站里面的业务非常的那么我们在处理的时候,也需要根据不同的路由规则来划分路由信息﹑例如:后台的路由(1.后台的首页2.后台的新闻中心3.后台的案例中心前台的路由)
新建routes目录创建相关js路由文件
1.2.1引入库
express官网-指南-路由
https://www.expressjs.com.cn/guide/routing.html
翻到差不多最下面express.Router代码复制到routes/index.js进行修改
app.get改成router.get,app.js里的相应代码删除
index.js
const express = require('express');
const router = express.Router(); // 路由器
//使用路由器定义映射规则
/*单独的路由文件,一般称之为:路由外置;路由外置文件里面的 路由url地址:app.use('/admin', indexRouter); + /index*/
router.get('/admin/index',(req,res)=>{
/*之前使用的 express-art-template 完成静态页面的渲染操作 */
res.render('index');
});
router.get('/admin/welcome',(req,res)=>{
res.render('welcome');
});
/* 暴露出去以后,谁需要这个路由信息,导入即可*/
module.exports = router;
website.js
const express = require('express');
const router = express.Router();
const WebsiteModel = require('../models/website.js');
//表单信息收集操作
router.post('/store', (req, res) => {
/*接受post提交的数据,然后在把数据入库操作 到底是更新还是添加操作 */
/* 如果是更新,则我们需要知道到底是更新哪条记录信息 */
console.log(req.body); // 就是post提交的数据 把数据库入库到数据库里面
//取想要的信息
let {_id, siteName, keyword, description, copyright, benanhao, address, phoneNumber, tel, email, qrcode, appcode} = req.body;
if(_id){
// 如果_id有值是更新操作,否则为添加操作
WebsiteModel.findById(_id, (error, data) => {
//找_id是否有错
if (error) {
res.send('update failure!');
return;
} else {
//更新 保存到数据库
data.siteName = siteName;
data.keyword = keyword;
data.description = description;
data.copyright = copyright;
data.benanhao = benanhao;
data.address = address;
data.phoneNumber = phoneNumber;
data.tel = tel;
data.email = email;
data.qrcode = qrcode;
data.appcode = appcode;
data.save((error, data) => {
if (error) {
console.log(error);
res.redirect('/admin/website/add');
} else {
console.log(data);
/*redirect翻译过来叫做重定向,也可以理解为跳转。参数就是我们跳转的地址*/
res.redirect('/admin/website/add');
}
});
}
})
}else{
//数据存储用mongoose 定义一个dingli的数据库
//表的定义
let webstiteObj = new WebsiteModel({
siteName,
keyword,
description,
copyright,
benanhao,
address,
phoneNumber,
tel,
email,
qrcode,
appcode
});
webstiteObj.save((error, data) => {
if (error) {
console.log(error);
res.redirect('/admin/website/add');
// res.send('error');
} else {
console.log(data);
res.redirect('/admin/website/add');
// res.send('ok');
}
});
}
});
// 网站基本信息模块添加
router.get('/add', async (req, res) => {
/*添加信息表单*/
const info = await WebsiteModel.findOne();
if (info) {
/* 如果信息是存在,则我们应该把信息给到页面做展示*/
// res.render('site-add', {info: info}); // {}
res.render('site-add', {info}); // {}
} else {
/* 如果不存在,应该是做信息的添加操作 */
res.render('site-add', {info: {}});
}
});
module.exports = router;
front.js
const express = require('express');
const router = express.Router(); // 路由器
const WebsiteModel = require('../models/website.js');
router.get('/', (req, res) => {
res.render('front/index');
})
router.get('/contact', async (req, res) => {
const info = await WebsiteModel.findOne();
res.render('front/contact', {info: info});
})
module.exports = router;
app.js增加
/*路由器引入*/
const indexRouter = require('./routes/index.js');
const websiteRouter = require('./routes/website.js');
const frontRouter = require('./routes/front.js');
app.use('/admin', indexRouter);
app.use('/admin/website', websiteRouter);
app.use(frontRouter);
1.3模板引擎外置
创建templateEngine目录 artTemplate.js
const path = require('path');
module.exports = (app) => {
app.engine('html', require('express-art-template'));
app.set('view options', {
debug: process.env.NODE_ENV !== 'production'
});
/* 注意:在使用的模板引擎目录的时候,需要设置为当前目录的上级目录 */
// console.log(path.join(__dirname, '../views'));
app.set('views', path.join(__dirname, '../views'));
app.set('view engine', 'html');
}
app.js增加 相关代码删除
const artTemplate = require('./templateEngine/artTemplate.js');
//定义art-template模板引擎
artTemplate(app);
二、提交信息校验
提交验证的验证操作
mongoose验证
http://www.mongoosejs.net/docs/validation.html
- 验证定义于 SchemaType
- 验证是一个中间件。它默认作为 pre(‘save’)` 钩子注册在 schema 上
- 你可以使用 doc.validate(callback) 或 doc.validateSync() 手动验证验证器不
- 对未定义的值进行验证,唯一例外是 required 验证器
- 验证是异步递归的。当你调用 Model#save,子文档验证也会执行,出错
- 话 Model#save 回调会接收错误
- 验证是可定制的
找到模板对models/website.js进行对应修改
/*引入数据库*/
const mongoose = require('../db/mongodb.js');
/*2. 这个是代码对网站配置表的操作,在开发的时候,一般我们会操作很多的表。那么我们需要在建立一个目录,对所有的表的模型进行管理。*/
const WebsiteSchema = mongoose.Schema({
siteName: {
type: String, // 定义类型
required: [true, '网站名称不能为空'], // 定义验证规则,这个字段不能为空,必填的
minlength: [10, '网站名称的最小长度为10个字符'], // 最小长度
maxlength: [30, '网站名称的最大长度为30个字符'], // 最大长度
},
keyword: {
type: String, // 定义类型
required: [true, '网站关键字信息不能为空'],
minlength: [10, '网站关键字信息的最小长度为10个字符'],
maxlength: [100, '网站关键字信息最大长度为100个字符'],
},
description: {
type: String, // 定义类型
required: [true, '网站描述信息不能为空'],
minlength: [10, '网站描述信息的最小长度为10个字符'],
maxlength: [200, '网站描述信息的最大长度为100个字符'],
},
copyright: String,
benanhao: String,
address: String,
phoneNumber: String,
tel: String,
email: String,
qrcode: String,
appcode: String,
});
const WebsiteModel = mongoose.model('Website', WebsiteSchema);
WebsiteModel.fields = {
siteName: '网站名称',
keyword: '网站关键字',
description: '网站描述',
copyright: '版权信息',
benanhao: '备案号',
address: '公司地址',
phoneNumber: '联系方式',
tel: '传真',
email: '网站邮箱',
qrcode: '公众号二维码',
appcode: '手机App二维码',
};
/* 等一下需要操作表的地方,需要使用的到模型,所以模型也要暴露 */
module.exports = WebsiteModel;
对routes/website.js进行对应修改获取错误信息
const express = require('express');
const router = express.Router();
const WebsiteModel = require('../models/website.js');
//表单信息收集操作
router.post('/store', (req, res) => {
let {_id, siteName, keyword, description, copyright, benanhao, address, phoneNumber, tel, email, qrcode, appcode} = req.body;
if(_id){
WebsiteModel.findById(_id, (error, data) => {
//找_id是否有错
if (error) {
res.send('update failure!');
return;
} else {
//验证信息是否合法
let error = data.validateSync();
if(error){
//如果有错
const errorRs = [];
for(let attr in error.errors){
errorRs.push(`${WebsiteModel.fields[attr]} 字段:${error.errors[attr]}`);
}
let url = '/admin/website/add';
let time = 3;
res.render('error', {errorRs,url, time});
return;
}
//更新 保存到数据库
data.siteName = siteName;
data.keyword = keyword;
data.description = description;
data.copyright = copyright;
data.benanhao = benanhao;
data.address = address;
data.phoneNumber = phoneNumber;
data.tel = tel;
data.email = email;
data.qrcode = qrcode;
data.appcode = appcode;
data.save((error, data) => {
if (error) {
console.log(error);
res.redirect('/admin/website/add');
} else {
console.log(data);
/*redirect翻译过来叫做重定向,也可以理解为跳转。参数就是我们跳转的地址*/
res.redirect('/admin/website/add');
}
});
}
})
}else{
let webstiteObj = new WebsiteModel({
siteName,
keyword,
description,
copyright,
benanhao,
address,
phoneNumber,
tel,
email,
qrcode,
appcode
});
let error = webstiteObj.validateSync();
// console.log('error',error);
if(error){
//如果有错
// console.log(error.errors);
const errorRs = [];
for(let attr in error.errors){
errorRs.push(`${WebsiteModel.fields[attr]} 字段:${error.errors[attr]}`);
}
// console.log(errorRs);
let url = '/admin/website/add';
let time = 3;
res.render('error', {errorRs,url, time});
}
else{
webstiteObj.save((error, data) => {
if (error) {
console.log(error);
res.redirect('/admin/website/add');
// res.send('error');
} else {
console.log(data);
res.redirect('/admin/website/add');
// res.send('ok');
}
});
}
}
});
// 网站基本信息模块添加
router.get('/add', async (req, res) => {
const info = await WebsiteModel.findOne();
if (info) {
/* 如果信息是存在,则我们应该把信息给到页面做展示*/
// res.render('site-add', {info: info}); // {}
res.render('site-add', {info}); // {}
} else {
/* 如果不存在,应该是做信息的添加操作 */
res.render('site-add', {info: {}});
}
});
module.exports = router;
如果提交时出错会跳转到出错页面 3秒后自动跳转回去
三、提交信息校验
3.1图片上传功能(单个)
第一步:HTML页面的input里的type改成file
<div class="layui-form-item">
<label for="qrcode" class="layui-form-label">
<span class="x-red">*</span>公众号二维码</label>
<div class="layui-input-inline">
<input type="file" id="qrcode" name="qrcode" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="appcode" class="layui-form-label">
<span class="x-red">*</span>手机端二维码</label>
<div class="layui-input-inline">
<input type="file" id="appcode" name="appcode" autocomplete="off" class="layui-input">
</div>
</div>
第二步:由于文件要上传 所以表单的编码enctype也需要修改一下
<form class="layui-form" action="/admin/website/store" method="post" enctype="multipart/form-data">
第三步:下载相关文件 查看使用方法
express官网-资源-中间件-Multer
https://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md
npm install --save multer
第四步:使用文件上传
在路由目录的website文件里引入文件上传相关
const path=require('path')
/*完成上传功能*/
const multer = require('multer')
//上传路径
const upload = multer({ dest: path.join(__dirname,'../public/uploads/' )})
因为是在store里做上传操作所以修改
router.post('/store', (req, res) => {
//改成
router.post('/store', upload.single('qrcode'),(req, res) => {
//上传文件的信息
console.log(req.file);
选择文件上传打印信息查看
但是这样保存的文件没有后缀名要手动添加、没有保存到数据库。
现在解决该问题:
解决后缀名
还是在之前给的链接里往下翻找到方法进行修改
const upload = multer({ dest: path.join(__dirname,'../public/uploads/' )})
//改成
const storage = multer.diskStorage({
destination: function (req, file, cb) {
//上传路径
cb(null, path.join(__dirname, '../public/uploads/'))
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
/*path.extname( file.originalname ) 可以获取到文件的扩展名 1.jpg==> .jpg*/
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
}
})
const upload = multer({ storage: storage })
再次尝试上传
保存数据库
外面已经定义了 qrcode 所以后面的 qrcode删除了
let qrcode ='';
//判断是否上传了图片
if(req.file){
//代表用户上传了图片
qrcode=req.file.filename;
}
let {_id, siteName, keyword, description, copyright, benanhao, address, phoneNumber, tel, email, appcode} = req.body;
上传后数据库查看:
3.2图片上传功能(多个)
router.post('/store', upload.fields([{name: 'qrcode'}, {name: 'appcode'}]), (req, res) => {
console.log(req.files); // 完成多文件上传功能
let codePaths = {qrcode: '', appcode: ''};
if (req.files) {
/* {qrcode: [{}], appcode: [{}]}*/
for (let attr in req.files) {
codePaths[attr] = req.files[attr][0].filename;
}
}
console.log(codePaths);
上传打印尝试,页面出错,但是取到了信息
信息存入数据库:
let {_id, siteName, keyword, description, copyright, benanhao, address, phoneNumber, tel, email} = req.body;
//由于我们在文件上传里面已经获取了qrcode和 appcode ,所以不能在从 body 里面获取字段
修改
data.qrcode = qrcode;
data.appcode = appcode;
//改成
data.qrcode = codePaths.qrcode;
data.appcode = codePaths.appcode;
let webstiteObj = new WebsiteModel({
...
qrcode,
appcode
});
qrcode,appcode//改成
qrcode: codePaths.qrcode,
appcode: codePaths.appcode,
上传多图
但是上传完成后,图片没有在后台页面展示,再进行修改:
在views目录下找到site-add.html进行图片展示修改
<div class="layui-form-item">
<label for="qrcode" class="layui-form-label">
<span class="x-red">*</span>公众号二维码</label>
<div>
<img src="/uploads/{{ info.qrcode }}" alt="">
</div>
<div class="layui-input-inline">
<input type="file" id="qrcode" name="qrcode" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label for="appcode" class="layui-form-label">
<span class="x-red">*</span>手机端二维码</label>
<div>
<img src="/uploads/{{ info.qrcode }}" alt="">
</div>
<div class="layui-input-inline">
<input type="file" id="appcode" name="appcode" autocomplete="off" class="layui-input">
</div>
</div>