网站建设价格标签,做网站招商需要具备什么,外贸官网建设,长沙大型做网站公司目录 项目效果
项目的搭建
编辑
响应静态网页
编辑
编辑
结合MongoDB数据库
结合API接口
进行会话控制 项目效果 该案例实现账单的添加删除查看#xff0c;用户的登录注册。功能比较简单#xff0c;但是案例主要是使用前段时间学习的知识进行实现的#xff0c…目录 项目效果
项目的搭建
编辑
响应静态网页
编辑
编辑
结合MongoDB数据库
结合API接口
进行会话控制 项目效果 该案例实现账单的添加删除查看用户的登录注册。功能比较简单但是案例主要是使用前段时间学习的知识进行实现的主要包括express服务的搭建以及使用并结合MongoDB数据库对数据进行存储以及操作同时编写相应的API接口最后进行会话控制确保数据的安全。如果以下的某一些部分感觉不太理解的话可以看我之前对应的文章。案例中使用到的知识点都是前面文章有涉及到的。
项目的搭建
首先我们直接使用express-generator来快速地搭建express应用骨架输入命令行express -e 文件名 然后使用npm i来进行项目依赖的下载。完成之后可以在package.json中对运行的命令 start: node./bin/www 修改为 start: nodemon ./bin/www这样后续的内容修改服务器就会自动地运行了。接下来运行npm start进行服务的启动。服务默认是监听3000端口我们输入http://127.0.0.1:3000进行访问。出现以下页面即服务搭建成功。 接下来我们需要对路由规则进行配置我们可以app.js文件中进行查看app.use(/, indexRouter);我们可以找到对应的路由导入var indexRouter require(./routes/index); 因此我们在routes文件夹下面的index.js文件进行路由的配置。
//index.jsvar express require(express);
var router express.Router();// 记账本列表
router.get(/account, function(req, res, next) {res.send(账单列表);
});// 记账本列表添加
router.get(/account/create, function(req, res, next) {res.send(添加记录);
});module.exports router;输入不同的路径得到不同的结构 响应静态网页
我们事先准备好了两个页面一个为账单页面一个为添加页面。我们借助res.rend()可以对ejs中的内容响应给浏览器的功能来进行操作。在views文件夹下面创建两个ejs文件。将账单页面以及添加页面加入。
//list.ejs!DOCTYPE html
html langenheadmeta charsetUTF-8 /meta http-equivX-UA-Compatible contentIEedge /meta nameviewport contentwidthdevice-width, initial-scale1.0 /titleDocument/titlelinkhrefhttps://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.cssrelstylesheet/stylelabel {font-weight: normal;}.panel-body .glyphicon-remove{display: none;}.panel-body:hover .glyphicon-remove{display: inline-block}/style/headbodydiv classcontainerdiv classrowdiv classcol-xs-12 col-lg-8 col-lg-offset-2h2记账本/h2hr /div classaccountsdiv classpanel panel-dangerdiv classpanel-heading2023-04-05/divdiv classpanel-bodydiv classcol-xs-6抽烟只抽煊赫门一生只爱一个人/divdiv classcol-xs-2 text-centerspan classlabel label-warning支出/span/divdiv classcol-xs-2 text-right25 元/divdiv classcol-xs-2 text-rightspanclassglyphicon glyphicon-removearia-hiddentrue/span/div/div/divdiv classpanel panel-successdiv classpanel-heading2023-04-15/divdiv classpanel-bodydiv classcol-xs-63 月份发工资/divdiv classcol-xs-2 text-centerspan classlabel label-success收入/span/divdiv classcol-xs-2 text-right4396 元/divdiv classcol-xs-2 text-rightspanclassglyphicon glyphicon-removearia-hiddentrue/span/div/div/div/div/div/div/div/body
/html//create.ejs
!DOCTYPE html
html langenheadmeta charsetUTF-8 /meta http-equivX-UA-Compatible contentIEedge /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title添加记录/titlelinkhref/css/bootstrap.cssrelstylesheet/link href/css/bootstrap-datepicker.css relstylesheet/headbodydiv classcontainerdiv classrowdiv classcol-xs-12 col-lg-8 col-lg-offset-2h2添加记录/h2hr /form methodpost action/accountdiv classform-grouplabel foritem事项/labelinputnametitletypetextclassform-controliditem//divdiv classform-grouplabel fortime发生时间/labelinputnametimetypetextclassform-controlidtime//divdiv classform-grouplabel fortype类型/labelselect nametype classform-control idtypeoption value-1支出/optionoption value1收入/option/select/divdiv classform-grouplabel foraccount金额/labelinputnameaccounttypetextclassform-controlidaccount//divdiv classform-grouplabel forremarks备注/labeltextarea nameremarks classform-control idremarks/textarea/divhrbutton typesubmit classbtn btn-primary btn-block添加/button/form/div/div/divscript src/js/jquery.min.js/scriptscript src/js/bootstrap.min.js/scriptscript src/js/bootstrap-datepicker.min.js/scriptscript src/js/bootstrap-datepicker.zh-CN.min.js/scriptscript src/js/main.js/script/body
/html然后我们修改原本的路由让其输入/account时为账单页表页面输入/account/create时为列表添加页面。
// 记账本列表
router.get(/account, function(req, res, next) {res.render(list);
});// 记账本列表添加
router.get(/account/create, function(req, res, next) {res.render(create);
}); 结合MongoDB数据库
接下来我们结合前几篇文章我们学习到的MongoDB数据库来对数据的添加删除以及读取等操作。如果不懂这一步操作的小伙伴看完前面发的文章。
我们在我们项目中创建三个文件夹configdb以及models。config文件用于对配置进行统一的设置在里面单独地设置域名端口以及数据库名等信息。db文件夹中创建db.js主要进行导入mongoose连接mongoose服务设置成功以及失败的回调等信息。module用于创建文档结构对象。
//config.js
module.exports{DBHOST:127.0.0.1,DBPORT:27017,DBNAME:bilibili,secret:atguigu
}
//db.js
module.exports function (success, error) {//导入配置文件const {DBHOST,DBPORT,DBNAME}require(../config/config);//导入mongooseconst mongoose require(mongoose);//连接mongodb服务mongoose.connect(mongodb://${DBHOST}:${DBPORT}/${DBNAME});mongoose.connection.once(open, () {success();})//设置连接错误的回调mongoose.connection.on(error, () {error();});//设置连接关闭的回调mongoose.connection.on(close, () {console.log(连接关闭);});
}接着再www文件中导入db.js中的函数在文件中调用函数传入两个函数成功的回调以及失败的回调成功的回调直接写原本启动http服务的代码确保数据库连接成功之后再启动http服务。失败的回调我们直接输出失败即可。
//www
const db require(../db/db);
//连接上数据库再来启动http服务
db((){//原本文件中的代码}
},(){console.log(连接失败)
})
接着想要操作数据库我们需要先准备好模型文件
//models/AccountModel.js
const mongoose require(mongoose);
//创建文档结构对象
let AccountSchema new mongoose.Schema({title: {type: String,required: true},time: Date,type: {type: Number,required: true},account: {type: Number,required: true},remarks: {type: String}
});
//创建文档模型对象
let AccountModel mongoose.model(accounts, AccountSchema);
//暴露模型对象
module.exports AccountModel;
模型文件准备好之后我们在对路由文件routes/index.js文件来进行操作在原本的文件中添加新增数据的操作
//新增记录
router.post(/account, function(req, res, next) {AccountModel.create({...req.body,time:moment(req.body.time).toDate()}).then(data{res.render(success,{msg:添加成功~~,url:/account});}).catch(err{res.status(500).send(插入失败~~);})
});
这里面使用到了一个moment包主要是用于对日期进行转换为对象形式方便后续的操作。需要在文件中导入const momentrequire(moment);
接着我们添加账单可以使用数据库可视化工具看到自己添加的数据我使用的是Navicat。新建连接选择Mongo连接成功之后就可以看到对应的数据库以及相应的集合。 以上就是我们所添加的数据但是我们需要在页表页面上也可以看到对应的数据我们在路由配置文件中进行读取数据的操作。
// 记账本列表
router.get(/account,function(req, res, next) {//获取所有账单信息AccountModel.find().sort({time:-1}).exec().then(data{res.render(list,{accounts:data,moment:moment});}).catch(err{res.status(500).send(读取失败~~)})});
接着需要修改list.ejs中的代码方便对数据进行展示
!DOCTYPE html
html langenbodydiv classcontainerdiv classrowdiv classcol-xs-12 col-lg-8 col-lg-offset-2div classrow text-rightdiv classcol-xs-12 stylepadding-top: 20px;form action/logout methodpostbutton classbtn btn-danger退出/button/form/div/divhrdiv classrowh2 classcol-xs-6记账本/h2h2 classcol-xs-6 text-righta href/account/create classbtn btn-primary添加账单/a/h2/divhr /div classaccounts% accounts.forEach(item { %div classpanel % item.type -1 ? panel-danger:panel-success %div classpanel-heading% moment(item.time).format(YYYY-MM-DD) %/divdiv classpanel-bodydiv classcol-xs-6% item.title %/divdiv classcol-xs-2 text-centerspan classlabel % item.type -1 ? label-warning:label-success %% item.type -1 ? 支出:收入 %/span/divdiv classcol-xs-2 text-right% item.account % 元/divdiv classcol-xs-2 text-righta classdelBtn href/account/% item._id %spanclassglyphicon glyphicon-removearia-hiddentrue/span/a/div/div/div% }) % /div/div/div/div/body
/html接着我们来对数据进行删除操作我们在list.ejs中对叉号绑定一个事件当用户确定删除时再进行删除数据防止数据误删。 div classcol-xs-2 text-righta classdelBtn href/account/% item._id %span classglyphicon glyphicon-remove aria-hiddentrue/span/a/divscriptlet delBtnsdocument.querySelectorAll(.delBtn);delBtns.forEach(item {item.addEventListener(click,function(e){if(confirm(您确定要删除该文档吗)){return true;}else{e.preventDefault();}})})/script
并在路由中设置删除的操作
//删除记录
router.get(/account/:id,(req,res){//获取params 的 id参数let idreq.params.id;//删除AccountModel.deleteOne({_id:id}).then(data{res.render(success,{msg:删除成功~~,url:/account});}).catch(err{res.status(500).send(删除失败~~)})});
结合API接口
当我们需要将项目推广到更多的客户端程序时而不仅仅是局限在我们的浏览器进行访问时我们就需要为它添加对应的API接口同样的前几篇文章也有介绍了API接口的详细内容。如果不是太了解的小伙伴可以回头去看看。
为了更好地区分我们在routes文件夹下创建一个名为web的文件将原本的路由配置文件放入其中再创建一个名为api的文件夹创建一个名为account.js的文件用于存放API路由的配置。文件路径修改之后需要修改引入该文件的路径。并在app.js中导入并使用
const accountRouterrequire(./routes/api/account);app.use(/api,accountRouter);
在api的路由文件中实现创建账单接口、删除账单接口、获取单条数据接口以及更新账单接口。对应代码如下
// /api/account.js
const express require(express);
const jwt require(jsonwebtoken);
//导入moment
const moment require(moment);
const AccountModel require(../../models/AccountModel);
//导入中间件
let checkTokenMiddleware require(../../middlewares/checkTokenMiddleware)
const router express.Router();// 记账本列表
router.get(/account, checkTokenMiddleware,function (req, res, next) {AccountModel.find().sort({ time: -1 }).exec().then(data {res.json({//响应码 code: 0000,//响应信息msg: 读取成功,//响应数据data: data})}).catch(err {res.json({//响应码 code: 1001,//响应信息msg: 读取失败,//响应数据data: null})})});// 记账本列表添加
router.get(/account/create,checkTokenMiddleware, function (req, res, next) {res.render(create);
});//新增记录
router.post(/account, checkTokenMiddleware,function (req, res) {AccountModel.create({...req.body,time: moment(req.body.time).toDate()}).then(data {res.json({//响应码 code: 0000,//响应信息msg: 创建成功,//响应数据data: data})}).catch(err {res.json({//响应码 code: 1002,//响应信息msg: 创建失败,//响应数据data: null})})});//删除记录
router.delete(/account/:id, checkTokenMiddleware,(req, res) {//获取params 的 id参数let id req.params.id;//删除AccountModel.deleteOne({ _id: id }).then(data {res.json({//响应码 code: 0000,//响应信息msg: 删除成功,//响应数据data: {}})}).catch(err {res.json({//响应码 code: 1003,//响应信息msg: 删除失败,//响应数据data: null})})
});
//获取当个账单信息
router.get(/account/:id, checkTokenMiddleware,(req, res) {//获取params 的 id参数let id req.params.id;//查询数据库AccountModel.findById(id).then(data {res.json({//响应码 code: 0000,//响应信息msg: 读取成功,//响应数据data: data})}).catch(err {res.json({//响应码 code: 1004,//响应信息msg: 读取失败,//响应数据data: null})})
});//更新单个账单信息
router.patch(/account/:id, checkTokenMiddleware,(req, res) {//获取params 的 id参数let id req.params.id;AccountModel.updateOne({ _id: id }, req.body).then(data {//再次查询数据库AccountModel.findById(id).then(data {res.json({//响应码 code: 0000,//响应信息msg: 更新成功,//响应数据data: data}).catch(err {res.json({//响应码 code: 1004,//响应信息msg: 读取失败,//响应数据data: null})})}).catch(err {res.json({//响应码 code: 1005,//响应信息msg: 更新失败,//响应数据data: null})})});})
module.exports router;那如何对我们写好的接口做测试呢在前面的文章中介绍了Apipost软件来测试接口我们来尝试一下发一个GET请求来获取表单的信息数据。成功得到对应的数据。 进行会话控制
接下来我们使用我们学过的session以及token来对数据进行保护。具体的知识点可以看我前几篇发的文章。
接下来我们为项目添加一个注册的页面在routes中web文件夹下创建一个auth.js文件用户配置注册以及登录时的session相关信息。我们同样使用模板引擎来响应注册页面。将该创建好的路由文件在app.js中进行导入以及使用。
创建一个reg.ejs文件
!DOCTYPE html
html langenheadmeta charsetUTF-8 /meta http-equivX-UA-Compatible contentIEedge /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title注册/titlelink hrefhttps://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css relstylesheet /
/headbodydiv classcontainerdiv classrowdiv classcol-xs-12 col-md-8 col-md-offset-2 col-lg-4 col-lg-offset-4h2注册/h2hr /form methodpost action/regdiv classform-grouplabel foritem用户名/labelinput nameusername typetext classform-control iditem //divdiv classform-grouplabel fortime密码/labelinput namepassword typepassword classform-control idtime //divhrbutton typesubmit classbtn btn-primary btn-block注册/button/form/div/div/div
/body/html
在响应注册页面
//注册
router.get(/reg,(req,res){res.render(auth/reg);
});
我们需要先创建用户模型后续才能对其进行插入数据库以及设置session等操作。
const mongoose require(mongoose);
//创建文档结构对象
let UserSchema new mongoose.Schema({username:String,password:String});
//创建文档模型对象
let UserModel mongoose.model(users, UserSchema);
//暴露模型对象
module.exports UserModel;
将该模型导入到对应的配置文件中并进行注册等相关操作
/导入用户模型
const UserModelrequire(../../models/UserModel);
const md5require(md5);//注册用户
router.post(/reg,(req,res){UserModel.create({...req.body,password:md5(req.body.password)}).then(data{res.render(success,{msg:注册成功,url:/login});}).catch(err{res.status(500).send(注册失败)})
});接下来实现用户登录功能我们同样创建一个login.ejs文件放置登录页面模板复制注册页面的代码将对应的文字以及路径修改一下即可。这部分不进行代码展示。进行登录的相关操作当用户登录之后我们需要对他的session进行写入,并返回sessionid。
//登录
router.get(/login,(req,res){res.render(auth/login);
});
//登录操作
router.post(/login,(req,res){//获取用户名和密码let {username,password}req.body;UserModel.findOne({username:username,password:md5(password)}).then(data{if(!data){return res.send(账号或者密码错误~~)}//写入sessionreq.session.usernamedata.username;req.session._iddata._id;//登录成功响应res.render(success,{msg:登录成功,url:/account});}).catch(err{res.status(500).send(登录失败)})
});这部分需要先安装express-session以及connect-mongo并在app.js中进行导入并设置中间件。
//导入 express-session
const session require(express-session);
const MongoStore require(connect-mongo);//导入配置项
const {DBHOST, DBPORT, DBNAME} require(./config/config);
//设置 session 的中间件
app.use(session({name: sid, //设置cookie的name默认值是connect.sidsecret: atguigu, //参与加密的字符串又称签名 加盐saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的idresave: true, //是否在每次请求时重新保存session 20 分钟 4:00 4:20store: MongoStore.create({mongoUrl: mongodb://${DBHOST}:${DBPORT}/${DBNAME} //数据库的连接配置}),cookie: {httpOnly: true, // 开启后前端无法通过 JS 操作maxAge: 1000 * 60 * 60 * 24 * 7 // 这一条 是控制 sessionID 的过期时间的},
}))
当我们登录成功之后我们可以在数据库中看到我们对应的session信息。 写入之后我们还需要判断用户是否登录若用户没有进行登录则拒绝访问跳转到登录页面。我们在web文件夹下的index.js中编写一个中间件。
//检测登录的中间件
const checkLoginMiddleware (req, res, next) {//判断if(!req.session.username){return res.redirect(/login);}next();}
并在下面的路由规则中使用它。这里只对查看记账本列表做演示其他的一致。
// 记账本列表
router.get(/account,checkLoginMiddleware,function(req, res, next) {AccountModel.find().sort({time:-1}).exec().then(data{res.render(list,{accounts:data,moment:moment});}).catch(err{res.status(500).send(读取失败~~)})});接下来继续在auth.js中实现退出登录功能。
//退出登录
router.post(/logout,(req,res){//销毁sessionreq.session.destroy((){res.render(success,{msg:退出成功,url:/login})})
})在退出登录界面部分进行修改防止CSRF跨站请求伪造。它会导致用户的session被获取。大部分的CSRF跨站请求伪造都是使用一个天生具有跨域能力的标签。但是它们发送的请求都是get请求因此我们将原本的退出修改为post请求。可以防止发生。
div classcol-xs-12 stylepadding-top: 20px;form action/logout methodpostbutton classbtn btn-danger退出/button/form
/div
我们接着在web文件夹下的index.js对首页添加路由规则
//添加首页路由规则
router.get(/,(req,res){//重定向res.redirect(/account);
});
在app.js中添加404的响应的模板在views中创建一个404.ejs文件。
// catch 404 and forward to error handler
app.use(function(req, res, next) {res.render(404);
});以上的操作我们使用了session对网页端进行了约束接下来我们使用token来对接口来进行约束。在api文件夹下创建一个auth.js文件对其进行设置。这部分就不再详细介绍了文件相应的代码如下
var express require(express);
var router express.Router();
//导入jwt
const jwtrequire(jsonwebtoken);
//读取配置项
const {secret} require(../../config/config);
//导入用户模型
const UserModelrequire(../../models/UserModel);
const md5require(md5);//登录操作
router.post(/login,(req,res){//获取用户名和密码let {username,password}req.body;UserModel.findOne({username:username,password:md5(password)}).then(data{if(!data){return res.json({code:2002,msg:用户名或者密码错误,data:null})}//创建当前用户tokenlet tokenjwt.sign({username:data.username,_id:data._id},secret,{expiresIn:60 * 60 * 24 *7});//响应tokenres.json({code:0000,msg:登录成功,data:token})}).catch(err{res.json({code:2001,msg:数据库读取失败,data:null})})
});//退出登录
router.post(/logout,(req,res){//销毁sessionreq.session.destroy((){res.render(success,{msg:退出成功,url:/login})})
})module.exports router;中间件文件
//checkTokenMiddleware.js
const jwtrequire(jsonwebtoken);
//读取配置项
const {secret} require(../config/config);
module.exports (req, res, next) {//获取tokenlet token req.get(token);//判断if (!token) {return res.json({code: 2003,msg: token 缺失,data: null})}//校验tokenjwt.verify(token, secret, (err, data) {if (err) {return res.json({code: 2004,msg: 校验失败,data: null})}//保存用户的信息req.userdata;//如果执行成功next();});}
通过Apipost来进行校验当没有携带token时获取不到数据当设置请求头token数据时能够获取到对应的数据。 好啦本文就到这里了Node.js系列的文章就告一段落了如果有不足之处还请见谅~~