看完node.js从入门到实践之后,进行更深入的学习。这篇主要是一个实战小demo。涉及到项目从无到有的过程,可能相对于实际的项目来说偏不成熟点,但对于新手入门还是可以很好的梳理项目开发的流程的。
- 新建项目文件夹nodeDemo,在该文件夹目录下
npm init
项目; - 安装express 插件
npm install express --save-dev
; - 为了自动监听项目变化,重启服务,全局安装nodemon
npm install nodemon
,之前安装过的请忽略此步操作,可以执行npm root -g
来获取全局安装的插件路径看看之前有无安装过 - 新建app.js
const express = require('express');
const app = express();
const port = 5000;
//配置路由
app.get('/',(req,res) => {
res.send('index');
})
app.get('/about', (req, res) => {
res.send('about');
})
app.listen(port,() =>{
console.log(`Server started on ${port}`);
});
模板引擎handlebars
handlebars官网
1.安装:npm install express-handlebars
github插件地址
2.引用并配置
//app.js
const exphbs = require('express-handlebars');//引入handlebars
//handlebars middleware
app.engine('handlebars', exphbs({ defaultLayout: 'main' }));//设置入口文件,文件路径为views/layouts/main.handlebars
//设置模板引擎
app.set('view engine', 'handlebars');
3.在项目文件夹中新建views文件夹,再在其中新建layouts文件夹并在内部新建main.handlebars文件。该文件是此项目html入口文件,在body标签中需要{{{body}}}
来引入,否则页面渲染不出来。注意,如果不定义该文件或路径错误,页面会报错。
使用公共模板
- 安装Bootstrap把对应的BootstrapCDN中的css以及js代码拷贝到main.handlebars文件中。
- 抽离公共组件
此处以导航为例,在views文件夹中新建partials
文件夹用来存放公共组件。例如在其中新建_navbar.handlebars组件后,只需要在引用的地方使用{{> _navbar}}
即可引入。
注意partials
文件夹的名称不能更改,内部文件命名方式可以更改,_文件名为handlebars的命名规则.
3.从路由传递参数
在app.js中修改路由配置项
//app.js
app.get('/',(req,res) => {
const title = '大家好,我是茕茕'
res.render('index',{title:title});
})
之后再在对应的文件中接收参数,此处以index.handlebars为例
//index.handlebars
<h1 class="display-3">{{title}}</h1>
前端添加页面&后端错误验证
1.express 版本在4以上是,不需要安装body-parser,直接引入var bodyParser = require('body-parser')
即可运用,否则需要npm install body-parser
。
//app.js
const bodyParser = require('body-parser');
//body-parser 中间件
var jsonParser = bodyParser.json()
var urlencodedParser = bodyParser.urlencoded({ extended: false })
2.添加表单页面,在views文件夹中新建ideas文件夹并新增add.handlebars文件
//add.handlebars
{{#each errors}}
<div class="alert alert-danger">{{text}}</div>
{{else}}
{{/each}}
<div class="card card-body">
<h3>想学的课程</h3>
<form action="/ideas" method="post">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" name="title" value="{{title}}">
</div>
<div class="form-group">
<label for="details">详情</label>
<textarea class="form-control" name="details">{{details}}</textarea>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
3.修改app.js文件
app.get('/ideas/add', (req, res) => {
res.render('ideas/add');
})
app.post('/ideas',urlencodedParser,(req, res) => {
// console.log(req.body);
let errors = [];
if(!req.body.title){
errors.push({text:'请输入标题!'})
}
if(!req.body.details){
errors.push({text:'请输入详情!'})
}
if(errors.length > 0){
res.render('ideas/add',{errors:errors,title:req.body.title,details:req.body.details});
}else{
res.send('ok');
}
})
安装mongoodb
创建数据模型
-
npm install mongoose --save-dev
在项目中安装mongoose - 在app.js中引入并连接mongoose
const mongoose = require('mongoose');//引入mongoose
//链接数据库
//‘mongodb://localhost’为本地数据库地址,‘node-app’为此项目链接的数据库名称为自定义名称
mongoose.connect('mongodb://localhost/node-app',{useNewUrlParser:true})
.then(() => {
console.log('链接成功!')
})
.catch(err => {
console.log(err);
})
3.在项目根目录下新建 models 文件夹 并添加Idea.js 文件(文件名首字母大写代表数据模型):
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const IdeaSchema = new Schema({
title: {
type:String,
require:true
},
details: {
type:String,
require:true
},
date: {
type:Date,
default:Date.now
},
})
mongoose.model('ideas',IdeaSchema);//将IdeaSchema放到模型中
4.在app.js中引入并使用()
//链接数据库
mongoose.connect('mongodb://localhost/node-app',{useNewUrlParser:true})
.then(() => {
console.log('链接成功!')
})
.catch(err => {
console.log(err);
})
//引入模型
require('./models/idea');
const Idea = mongoose.model('ideas')
存储和拉取数据
在app.js中修改并运行项目添加一条记录到数据库中
app.post('/ideas',urlencodedParser, (req, res) => {
console.log(req.body);
let errors = [];
if(!req.body.title){
errors.push({text:'请输入标题!'});
}
if (!req.body.details) {
errors.push({ text: '请输入详情!' });
}
if (errors.length > 0){
res.render('ideas/add',{
errors:errors,
title: req.body.title,
details: req.body.details
});
}else{
// res.send('ok')
const newUser = {
title: req.body.title,
details: req.body.details
}
new Idea(newUser).save()
.then(idea => {
res.redirect('/ideas')//跳转到对应的地址
})
}
2.找到mongoose的文件夹,进入到 ****\MongoDB\Server\4.0\bin 文件中,使用命令行
./mongo
show dbs
找到当前数据库
use node-app
切换到数据库下面(node-app为数据库名)
show collections
找到数据库中的列表,此时展示的为ideas数据表
db.ideas.fond()
查询列表所有内容
3.添加并展示数据
//app.js中配置路由并传递参数
app.get('/ideas', (req, res) => {
Idea.find({})
.sort({date:'desc'})//降序排列
.then(ideas => {
res.render('ideas/index',{ideas:ideas});
})
})
在ideas文件夹中新建index.handlebars文件:
{{#each ideas}}
<div class="card card-body mb-2">
<h3>{{title}}</h3>
<p>{{details}}</p>
</div>
{{else}}
<p>还没有任何想学的课程</p>
{{/each}}
编辑页面(拉取/存储数据)
- 修改ideas文件夹中新建index.handlebars文件:
{{#each ideas}}
<div class="card card-body mb-2">
<h3>{{title}}</h3>
<p>{{details}}</p>
<a class="btn btn-dark btn-block" href="/ideas/edit/{{id}}">编辑</a>
</div>
{{else}}
<p>还没有任何想学的课程</p>
{{/each}}
- 添加对应的路由
//编辑
app.get('/ideas/edit/:id', (req, res) => {
Idea.findOne({_id:req.params.id})
.then(idea => {
res.render('ideas/edit',{
idea:idea
});
})
})
- ideas文件夹中新建edit.handlebars文件
{{#each errors}}
<div class="alert alert-danger">{{text}}</div>
{{else}}
{{/each}}
<div class="card card-body">
<h3>想学的课程</h3>
<form action="/ideas" method="post">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" name="title" value="{{idea.title}}">
</div>
<div class="form-group">
<label for="details">详情</label>
<textarea class="form-control" name="details">{{idea.details}}</textarea>
</div>
<button type="submit" class="btn btn-primary">编辑</button>
</form>
</div>
- 安装method-override 允许您在客户端不支持它的地方使用HTTP动词,如PUT或DELETE。
命令行 :npm install method-override
method-override文档 - 引用 method-override
//app.js
const methodOverride = require('method-override');
//method-override 中间件
app.use(methodOverride('_method'))
在edit.handlebars文件使用,修改form标签action的属性以及添加一个隐藏的input并设置成put提交形式
{{#each errors}}
<div class="alert alert-danger">{{text}}</div>
{{else}}
{{/each}}
<div class="card card-body">
<h3>想学的课程</h3>
<form action="/ideas/{{idea.id}}?_method=PUT" method="post">
<input type="hidden" name="_method" value="PUT">
<div class="form-group">
<label for="title">标题</label>
<input type="text" class="form-control" name="title" value="{{idea.title}}">
</div>
<div class="form-group">
<label for="details">详情</label>
<textarea class="form-control" name="details">{{idea.details}}</textarea>
</div>
<button type="submit" class="btn btn-primary">编辑</button>
</form>
</div>
6.添加编辑接口
//app.js
//编辑
app.put('/ideas/:id',urlencodedParser,(req,res) => {
// res.send('PUT');
Idea.findOne({
_id:req.params.id
})
.then(idea => {
idea.title = req.body.title;
idea.details = req.body.details;
idea.save()
.then(() => {
res.redirect('/ideas')
})
})
})
删除数据
- 修改index.handlebars文件,添加删除按钮:
//index.handlebars
{{#each ideas}}
<div class="card card-body mb-2">
<h3>{{title}}</h3>
<p>{{details}}</p>
<a class="btn btn-dark btn-block" href="/ideas/edit/{{id}}">编辑</a>
<form action="/ideas/{{id}}?_method=DELETE" method="POST">
<input type="hidden" name="method" value="DELETE" />
<input type="submit" class="btn btn-danger btn-block" value="删除"/>
</form>
</div>
{{else}}
<p>还没有任何想学的课程</p>
{{/each}}
- 修改app.js,添加删除功能:
//删除
app.delete('/ideas/:id',(req,res) => {
console.log(req.body)
Idea.remove({_id:req.params.id})
.then(() => {
res.redirect('/ideas');
})
})
对用户的操作进行提醒
- 安装 express-session ,用于存储内容
npm install express-session
//app.js
const session = require('express-session');
//express-session 中间件
app.use(session({
secret: 'secret',//秘钥,自定义
resave: true,
saveUninitialized: true,
}))
- 安装connect-flash
npm install connect-flash
//app.js
const flash = require('connect-flash');
//flash 中间件
app.use(flash());
3.配置全局变量
//app.js
//配置全局变量
app.use((req,res,next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
next();
});
4.在partials 文件夹中新建文件msg.handlebars文件编写信息提示
{{#if success_msg}}
<div class="alert alert-success">{{success_msg}}</div>
{{/if}}
{{#if error_msg}}
<div class="alert alert-danger">{{error_msg}}</div>
{{/if}}
5.在main.handlebars文件中引用
<!-- 引用导航 -->
{{> _navbar}}
<div class="container">
<!-- 引用信息提示 -->
{{> _msg}}
{{{body}}}
</div>
6.最后在需要提示的功能出调用flash方法,第一个参数是定义的方法名,第二个参数是传递的信息,例如:
//删除
app.delete('/ideas/:id',(req,res) => {
console.log(req.body)
Idea.remove({_id:req.params.id})
.then(() => {
req.flash('success_msg','数据删除成功!');
res.redirect('/ideas');
})
})
抽离代码
- 在项目根路径下创建routes文件夹,并新建 ideas.js 和 users.js 文件
- 将app.js中关于课程的接口剪切到ideas.js中,并引入相关的插件以及对应的中间件
- 在ideas.js定义
const router = express.Router();
,并替换接口方法中app为router - 最后
module.exports = router;
- 在app.js中加载ideas.js路由
const ideas = require('./routes/ideas')
,并使用app.use('/ideas',ideas);
app.use()第一个参数设置成‘/’时,路由中ideas的原文件不变,设置成‘/ideas’时,原文件接口中的‘/ideas’可全部删除 - users.js 模块抽离亦然。
//app.js
const express = require('express');
const exphbs = require('express-handlebars');//引入handlebars
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const methodOverride = require('method-override');
const session = require('express-session');
const flash = require('connect-flash');
const app = express();
//加载路由
const ideas = require('./routes/ideas')
const users = require('./routes/users')
//链接数据库
mongoose.connect('mongodb://localhost/node-app',{useNewUrlParser:true})//‘mongodb://localhost’为本地数据库地址,‘node-app’为此项目链接的数据库名称为自定义名称
.then(() => {
console.log('数据库链接成功!')
})
.catch((err) => {
console.log(err);
})
//引入模型
require('./models/Idea');
const Idea = mongoose.model('ideas')
//handlebars middleware
app.engine('handlebars', exphbs({ defaultLayout: 'main' }));//设置入口文件,文件路径为views/layouts/main.handlebars
//设置模板引擎
app.set('view engine', 'handlebars');
//body-parser 中间件
var jsonParser = bodyParser.json()
var urlencodedParser = bodyParser.urlencoded({ extended: false })
//method-override 中间件
app.use(methodOverride('_method'));
//session 中间件
app.use(session({
secret: 'secret',//秘钥,自定义
resave: true,
saveUninitialized: true,
}))
//flash 中间件
app.use(flash());
//配置全局变量
app.use((req,res,next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
next();
});
//配置路由
app.get('/',(req,res) => {
const title = '大家好,我是茕茕'
res.render('index',{title:title});
})
app.get('/about', (req, res) => {
res.render('about');
})
app.get('/ideas/add', (req, res) => {
res.render('ideas/add');
})
//使用routes
app.use('/ideas',ideas);//第一个参数设置成‘/’时,路由中ideas的原文件不变,设置成‘/ideas’时,原文件接口中的‘/ideas’可全部删除不用
app.use('/users',users);
//监听
const port = 5000;
app.listen(port,() =>{
console.log(`Server started on ${port}`);
});
//ideas.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const router = express.Router();
//引入模型
require('../models/Idea');
const Idea = mongoose.model('ideas')
//body-parser 中间件
var jsonParser = bodyParser.json()
var urlencodedParser = bodyParser.urlencoded({ extended: false })
router.get('/', (req, res) => {
Idea.find({})
.sort({date:'desc'})//降序排列
.then(ideas => {
res.render('ideas/index',{ideas:ideas});
})
})
//编辑
router.get('/edit/:id', (req, res) => {
Idea.findOne({_id:req.params.id})
.then(idea => {
res.render('ideas/edit',{
idea:idea
});
})
})
//添加
router.post('/',urlencodedParser,(req, res) => {
// console.log(req.body);
let errors = [];
if(!req.body.title){
errors.push({text:'请输入标题!'})
}
if(!req.body.details){
errors.push({text:'请输入详情!'})
}
if(errors.length > 0){
res.render('ideas/add',{errors:errors,title:req.body.title,details:req.body.details});
}else{
// res.send('ok');
const newUser = {
title: req.body.title,
details: req.body.details
}
new Idea(newUser).save()
.then(idea => {
req.flash('success_msg','数据添加成功!');
res.redirect('/ideas')//跳转到对应的地址
})
}
})
//编辑
router.put('/:id',urlencodedParser,(req,res) => {
// res.send('PUT');
Idea.findOne({
_id:req.params.id
})
.then(idea => {
idea.title = req.body.title;
idea.details = req.body.details;
idea.save()
.then(() => {
req.flash('success_msg','数据编辑成功!');
res.redirect('/ideas')
})
})
})
//删除
router.delete('/:id',(req,res) => {
console.log(req.body)
Idea.remove({_id:req.params.id})
.then(() => {
req.flash('success_msg','数据删除成功!');
res.redirect('/ideas');
})
})
module.exports = router;
登录注册页面设计
- 在views文件夹中新建users文件夹并新增login.handlebars 和 register.handlebars 文件,具体代码此处省略
- 在根目录中新建public文件夹,再在其中新增img文件夹和css文件夹,存储样式以及图片等静态资源
- 在app.js中引入path并设置静态资源,使其能在node中正常使用
const path = require('path')
//使用静态文静
app.use(express.static(path.join(__dirname,'public')));
- 最后在main.handlebars文件中引入css样式表
<link rel="stylesheet" href="/css/style.css">
注册页面验证
- 在users.js中完成验证
router.post('/register',urlencodedParser,(req,res) => {
// console.log(req.body);
// res.send('注册')
let errors = [];
if(req.body.password != req.body.password2){
errors.push({text:'两次密码不一致!'})
}
if(req.body.password.length < 4){
errors.push({text:'密码长度不能小于4!'})
}
if(errors.length > 0){
//有误
res.render('users/register',{
errors:errors,
name:req.body.name,
email:req.body.email,
password:req.body.password,
password2:req.body.password2
})
}else{
res.send('验证成功!')
}
})
- 将报错信息组件化并在main.handlebars文件中引用
注册页面数据存储
- 注册页面数据存储涉及到密码加密,所以首先需要安装密码加密插件:
npm install bcrypt
- 在users.js中引入
const bcrypt = require('bcrypt');
- 使用
//注册
router.post('/register',urlencodedParser,(req,res) => {
// console.log(req.body);
// res.send('注册')
let errors = [];
if(req.body.password != req.body.password2){
errors.push({text:'两次密码不一致!'})
}
if(req.body.password.length < 4){
errors.push({text:'密码长度不能小于4!'})
}
if(errors.length > 0){
//有误
res.render('users/register',{
errors:errors,
name:req.body.name,
email:req.body.email,
password:req.body.password,
password2:req.body.password2
})
}else{
// res.send('验证成功!')
//是否已注册
User.findOne({email:req.body.email})
.then((user) => {
if(user){
req.flash('error_msg','邮箱已存在,请更换邮箱注册!');
res.redirect('/users/register');
}else{
let newUser = new User({
name:req.body.name,
email:req.body.email,
password:req.body.password,
})
bcrypt.genSalt(10, function(err, salt) {//密码强度,回调函数
bcrypt.hash(newUser.password, salt,(err, hash) =>{//需要加密项,回调函数
if(err) throw err;
newUser.password = hash;//保存加密结果
newUser.save()
.then( (user) => {
req.flash('success_msg','账号注册成功!');
res.redirect('/users/login');
})
.catch( (err) => {
req.flash('error_msg','账号注册失败!');
res.redirect('/users/register');
})
});
});
}
})
}
})
登录验证
主要验证代码如下:
//登录
router.post('/login',urlencodedParser,(req,res) => {
// console.log(req.body);
//查询数据库
User.findOne({email:req.body.email})
.then((user) => {
if(!user){
req.flash('error_msg','用户不存在!')
res.redirect('/users/login');
return false;
}
//密码验证
bcrypt.compare(req.body.password, user.password, function(err, isMatch) {//前台录入密码,后台获取密码,回调函数
// res == true
if(err) throw err;
if(isMatch){
//验证成功
req.flash('success_msg','登录成功!');
res.redirect('/ideas');
}else{
req.flash('error_msg','密码错误!')
res.redirect('/users/login');
}
});
})
})
使用passport实现登录验证
使用原因:在登录之后,可以将登录状态保存并将登录注册按钮隐藏,显示登录后的列表页。但是一般的方法:封装一个全局变量,当登录后再改变这个变量的方法来处理,但这个变量的状态不能持久化。所以现在采用passport来实现。
1.安装npm install passport-local
npm install passport
- 在app.js中引用
const passport = require('passport');
require('./config/passport')(passport);
- 修改routes/users.js登录功能代码
//登录
router.post('/login',urlencodedParser,(req,res,next) => {
//‘local’为passport实例化的内容
passport.authenticate('local', {
failureRedirect: '/users/login',//验证失败后跳转的页面
successRedirect: '/ideas',//验证成功后跳转的页面
failureFlash: true,//失败报错开启
})(req,res,next)
//查询数据库
// User.findOne({email:req.body.email})
// .then((user) => {
// if(!user){
// req.flash('error_msg','用户不存在!')
// res.redirect('/users/login');
// return false;
// }
// //密码验证
// bcrypt.compare(req.body.password, user.password, function(err, isMatch) {//前台录入密码,后台获取密码,回调函数
// // res == true
// if(err) throw err;
// if(isMatch){
// //验证成功
// req.flash('success_msg','登录成功!');
// res.redirect('/ideas');
// }else{
// req.flash('error_msg','密码错误!')
// res.redirect('/users/login');
// }
// });
// })
})
- 在根目录中新建config 文件夹并新建passport.js
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
//加载model
require('../models/Users')
const User = mongoose.model('users');
module.exports = (passport) => {
passport.use(new LocalStrategy(
{usernameField:'email'},//验证的对象
(email,password,done) => {
// console.log(email,password);
//查询数据库
User.findOne({email:email})
.then((user) => {
if(!user){
return done(null,false,{message:"没有这个用户!"});//是否传对应的内容,得到对应的user,出现错误时的提示
}
//密码验证
bcrypt.compare(password, user.password, function(err, isMatch) {//前台录入密码,后台获取密码,回调函数
// res == true
if(err) throw err;
if(isMatch){
//验证成功
return done(null,user);//是否传对应的内容,得到对应的user,出现错误时的提示
}else{
return done(null,user,{message:"密码错误!"});//是否传对应的内容,得到对应的user,出现错误时的提示
}
});
})
}
));
}
- 最后新增一个全局变量来显示错误信息,在app.js中修改全局变量
//配置全局变量
app.use((req,res,next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
- 修改views/partials/_errors.handlebars文件
{{#if error}}
<div class="alert alert-danger">{{error}}</div>
{{/if}}
{{#each errors}}
<div class="alert alert-danger">{{text}}</div>
{{else}}
{{/each}}
导航守卫和退出登录
完成以上操作后,虽然我们可以在登录时作出判断,但是当验证通过后,我们不能跳转到对应的页面,并且控制台会打印错误。
- 在app.js 中session 中间件下面引用,注意一定要在session 中间件下面,不然会报错
//session 中间件
app.use(session({
secret: 'secret',//秘钥,自定义
resave: true,
saveUninitialized: true,
}))
app.use(passport.initialize());
app.use(passport.session());
- 在passport.js中添加序列化和反序列化代码,保证登录状态持久
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
//加载model
require('../models/Users')
const User = mongoose.model('users');
module.exports = (passport) => {
passport.use(new LocalStrategy(
{usernameField:'email'},//验证的对象
(email,password,done) => {
// console.log(email,password);
//查询数据库
User.findOne({email:email})
.then((user) => {
if(!user){
return done(null,false,{message:"没有这个用户!"});//是否传对应的内容,得到对应的user,出现错误时的提示
}
//密码验证
bcrypt.compare(password, user.password, function(err, isMatch) {//前台录入密码,后台获取密码,回调函数
// res == true
if(err) throw err;
if(isMatch){
//验证成功
return done(null,user);//是否传对应的内容,得到对应的user,出现错误时的提示
}else{
return done(null,user,{message:"密码错误!"});//是否传对应的内容,得到对应的user,出现错误时的提示
}
});
})
}
));
//序列化和反序列化 - 保证登录状态持久化
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
}
此时登录操作完全结束,接下来我们开始写退出登录逻辑。
- 先在app.js中添加一个全局变量,来控制导航上登录注册按钮的显示状态
//配置全局变量
app.use((req,res,next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
res.locals.user = req.user || null;
next();
});
- 修改_navbar.handlebars文件导航右侧html结构
<ul class="navbar-nav ml-auto">
{{#if user}}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" id="navbarDropdownMenuLink">想学的课程</a>
<div class="dropdown-menu">
<a href="/ideas" class="dropdown-item">Idea</a>
<a href="/ideas/add" class="dropdown-item">添加</a>
</div>
</li>
<li class="nav-item">
<a href="/users/logout" class="nav-link">退出</a>
</li>
{{else}}
<li class="nav-item">
<a href="/users/login" class="nav-link">登录</a>
</li>
<li class="nav-item">
<a href="/users/register" class="nav-link">注册</a>
</li>
{{/if}}
</ul>
- 在routes/users.js中添加退出功能
//退出登录
router.get('/logout',(req,res) => {
req.logout();
req.flash('success_msg','退出成功!');
res.redirect('/users/login');
})
现在退出登录的功能也完善啦,接下来就是添加导航守卫,为了防止在没登录的情况下,故意更改路由直接访问其他页面。
- 在根路径下新建helps文件件并新建auth.js文件
//授权守卫
module.exports = {
ensureAuthenticated:(req,res,next) => {
if(req.isAuthenticated()){
return next();
}else{
req.flash('error_msg','请先登录!');
res.redirect('/users/login');
}
}
}
- 在需要用到导航守卫的地方进行引用,我们这里需要导航守卫的地方为ideas页面下,所以在routes/ideas中引入
const {ensureAuthenticated} = require('../helps/auth')
//在所有的get和delete接口中添加ensureAuthenticated导航守卫,如下:
router.get('/add',ensureAuthenticated,(req, res) => {
res.render('ideas/add');
})
//删除
router.delete('/:id',ensureAuthenticated,(req,res) => {
console.log(req.body)
Idea.remove({_id:req.params.id})
.then(() => {
req.flash('success_msg','数据删除成功!');
res.redirect('/ideas');
})
})
显示自己所添加的数据
到目前为止,登录注册导航守卫已经全部完成,但是不同的用户登录之后看到的列表数据完全相同,并都有权限去修改,这样的操作肯定是不合理的,接下来就让不同的用户登录后只能查看并操作自己的数据吧~
- 在models/idea.js中添加user字段
const IdeaSchema = new Schema({
title: {
type:String,
require:true
},
user:{
type:String,
require:true
},
details: {
type:String,
require:true
},
date: {
type:Date,
default:Date.now
},
})
- 在routes/ideas中修改
router.get('/',ensureAuthenticated, (req, res) => {
Idea.find({user:req.user.id})
.sort({date:'desc'})//降序排列
.then(ideas => {
res.render('ideas/index',{ideas:ideas});
})
})
//编辑
router.get('/edit/:id',ensureAuthenticated,(req, res) => {
Idea.findOne({_id:req.params.id})
.then(idea => {
if(idea.user != req.user.id){
req.flash('error_mag','非法操作!');
res.redirect('/ideas');
}else{
res.render('ideas/edit',{
idea:idea
});
}
})
})
//添加
router.post('/',urlencodedParser,(req, res) => {
// console.log(req.body);
let errors = [];
if(!req.body.title){
errors.push({text:'请输入标题!'})
}
if(!req.body.details){
errors.push({text:'请输入详情!'})
}
if(errors.length > 0){
res.render('ideas/add',{errors:errors,title:req.body.title,details:req.body.details});
}else{
// res.send('ok');
const newUser = {
title: req.body.title,
details: req.body.details,
user:req.user.id
}
new Idea(newUser).save()
.then(idea => {
req.flash('success_msg','数据添加成功!');
res.redirect('/ideas')//跳转到对应的地址
})
}
})
项目打磨
- heroku 注册账号
- 微调项目代码
//package.js 修改启动命令
"scripts": {
"start": "node app.js"
},
//app.js 配置端口号
const port = process.env.PORT || 5000;
- 配合数据库
config文件夹中新建database.js
//配置数据库
if(process.env.NODE_ENV == 'production'){
//生产环境
module.exports = {
mongoURL:'线上数据库地址'
}
}else{
//开发环境
module.exports = {
mongoURL:'mongodb://localhost/node-app'
}
}
app.js 中引用
const db = require('./config/database')
//链接数据库
mongoose.connect(db.mongoURL,{useNewUrlParser:true})//‘mongodb://localhost’为本地数据库地址,‘node-app’为此项目链接的数据库名称为自定义名称
.then(() => {
console.log('数据库链接成功!')
})
.catch((err) => {
console.log(err);
})
课程总结及引导
1.在根目录中新建.gitignore文件配置忽略文件
之后就是部署上线了。但是由于不明原因 heroku 无法登录注册,以下步骤就省略吧。有兴趣的可以自己尝试其他部署。
heroku-cli