博客项目--零散知识点(环境变量、config、art-template、express、locals、Joi、bcryptjs、JSON、session、cookie、错误处理中间件、路由拦截)

通过process.env.环境变量名  来读取这个系统变量名的值

例如:我们添加环境变量NODE_ENV值为development来表示开发环境

if(process.env.NODE_ENV=='development'){
    console.log('开发环境');
}else{
    console.log('生产环境');
}

 不使用 config 来配置数据库的连接密码容易暴露,不安全

mongoose.connect('mongodb://xibing_G:xibing@localhost:27017/blog', { useNewUrlParser: true, useUnifiedTopology: true })

第三方模块:config 用于根据环境(开发环境or生产环境)来切换不同的代码

插件要求:项目根目录下创建config文件夹,并在内部建立以下文件:

development.json:如果是开发环境,将会执行这里面的,参数也将从这里获取

production.json:如果是生产环境,将会执行这里面的,参数将从这个文件中获取

default.json:对应的开发or生产环境文件中没有所需数据,将会来这里查询获取,默认环境

custom-environment-variables.json在这个文件中映射环境变量和应用配置的关系

【文件名一定不能有错误,不然就会有错误或者config功能使用无效】

使用模块内部提供的get方法获取配置信息,运行时config会按名称进行查找,读取其值

例如:

建立系统变量APP_PWD变量值为123456(将数据库的连接密码写到了系统变量中,相对安全

custom-environment-variables.json代码:会根据APP_PASSWORD查询系统变量对应的值给pwd

{
    "db":{
        "pwd":"APP_PASSWORD"
    }
}

development.json代码:

{
	"title":"博客管理系统-开发环境",
	"db":{
		"user":"xibing_G",
		"host":"localhost",
		"port":"27017",
		"name":"blog"
	}
}

 使用config来配置数据库的连接(connect.js代码)

// 连接数据库,引入mongoose第三方模块
const mongoose = require('mongoose');
// 导入config模块,获取config的json文件中所配置的信息
const config=require('config');
// 避免一个警告的提示
mongoose.set('useCreateIndex', true);
// 连接数据库
mongoose.connect(`mongodb://${config.get('db.user')}:${config.get('db.pwd')}@${config.get('db.host')}:${config.get('db.port')}/${config.get('db.name')}`, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('数据库连接成功'))
    .catch((err) => console.log('数据库连接失败' + err))

第三方模块:morgan  是express模块的中间件函数,来获取客户端向服务器端发送的请求信息

包括:请求方式、请求地址、请求状态码、请求时间等

morgan()函数可以将获取到的请求信息在控制台中输出【morgan('dev'),参数dev为固定写法】

const morgan=require('morgan');

app.use(morgan('dev'));

配置session  设置cookie的缓存有效时间

session中间的两个参数如果不填写会有警告提示,但也算不上报错

app.use(session({resave: false, //添加 resave 选项
  	saveUninitialized: false, 
    //添加 saveUninitialized,当用户没有登陆的时候不执行默认的保存cookie的行为
  	secret:'secret key',
  	// 设置cookie的过期时间,单位是毫秒,下面限制为1天的写法
	cookie:{
		maxAge:24*60*60*1000
	}
}));

处理 post 请求参数(非json类型的)

false表示使用querystring处理请求参数(官方推荐);true表示使用第三方qs模块处理

app.use(bodyParser.urlencoded({extended:false}));

配置 express 模板

这里的path是引用了系统模块path,目的使用其join来做路径拼接

// 告诉express框架模板所在位置(views为固定参数)
app.set('views', path.join(__dirname, 'views'));

// 告诉express框架模板的默认后缀(view engine为固定参数)
app.set('view engine', 'art');

// 告诉express框架渲染后缀为art时,所用的模板引擎是什么
app.engine('art', require('express-art-template'));

// 开放静态资源文件
app.use(express.static(path.join(__dirname, 'public')))

配置 art-template 模板引擎的全局时间日期格式化

借助第三方模块:dateformat

// 导入art-template模板引擎
const template=require('art-template');

// 导入dateformat第三方模块来对全局进行时间格式化
const dateFormat=require('dateformat');

// 向模板内部导入dateFormat变量,便于全局配置时间格式
template.defaults.imports.dateFormat=dateFormat;

配置完成后,由于是全局,所以所有的模板文件中都可以使用dataFormat来格式化日期/时间 

dateFormat(date,'yyyy-mm-dd');

两个参数:1、日期对象;2、目标格式 ;返回格式化后的日期


公共数据为避免每次从数据库中进行查询的麻烦,可以存放到 express实例的 locals

let user=await User.findOne({email:email});

req.app.locals.userInfo=user;

在添加用户的信息时使用第三方模块  Joi  进行数据格式的验证

// 验证用户信息
const validateUser=(user)=>{
    // 建立Joi验证对象(数据库有哪些字段需要验证就填写哪些验证名,并在其后编写验证规则)
    const schema=Joi.object({
        username:Joi.string().min(2).max(12).required().error(new Error('用户名不符合规则')),
        email:Joi.string().email().required().error(new Error('邮箱不符合规则')),
        password:Joi.string().regex(/^[a-zA-Z0-9]{3,20}$/).required().error(new Error('密码不符合验证规则')),
        role:Joi.string().valid('normal','admin').required().error(new Error('角色不符合验证规则')),
        //只能传入这两个值中的数据,其他数据不通过,类似于枚举
        state:Joi.number().valid(0,1).required().error(new Error('状态值非法'))
    });

    // 建立的Joi验证对象下会有validateAsync()来对传入的数据进行验证,return返回这个验证结果
    return schema.validateAsync(user);
}

添加用户时调用验证方法 validateUser()

// 实施验证
	try{
		// user信息通过post请求方式传递,采用req.body来获取参数(body功能源于'body-parser'模块)
		await validateUser(req.body);
	}catch(err){
		// 验证没有通过那重定向到添加用户页面
		return next(JSON.stringify({path:'/admin/user-edit',message:err.message}));
	}

对于 next() 错误处理中间件 的完善 借助 JSON.parseJSON.stringify

(验证用户信息出错)完善前:

return res.next(`/admin/user-edit?message=${err.message}`);

(错误处理中间件)完善前:

// 错误处理中间件
app.use((err,req,res,next)=>{
	// 将字符串再转回对象JSON.parse()
	const result = JSON.parse(err);
	res.redirect(`${result.path}?message=${result.message}`);
})

( 验证用户信息出错)完善后:

// next方法只能传递一个参数,并且是字符串类型
// 由于重定向语句用到了  重定向地址  和  携带参数  这两个变化的量
// 所以需要传递两个参数,将两个参数放到对象中,再将对象转为字符串传递到next中
return next(JSON.stringify({path:'/admin/user-edit',message:err.message}));

(错误处理中间件)完善后:

// 错误处理中间件
app.use((err,req,res,next)=>{
	// 将字符串再转回对象JSON.parse()
	const result = JSON.parse(err);

	// 对重定向代码进行改进,比如在user-modify中除了path、message还有传入id
	// path是必要的,但是后面的参数需要灵活变动
	// 新建数组用于存放后面的参数
	let params=[];
	for(let attr in result){
		// 又因为path在result中,所以我们把path进行刨除
		if(attr!=path){
			//attr+'='+result.[attr];相当于message='密码比对失败'
			params.push(attr+'='+result[attr]);
		}
	}
	// 多个参数中间需要用&隔开,所以借助数组的join方法,对每一项都用&进行分隔连接
	res.redirect(`${result.path}?${params.join('&')}`);
})

密码加密 第三方模块 bcryptjs  (相比bcrypt功能相同,还不用下载安装一些依赖)

// 引入加密模块
const bcrypt=require('bcryptjs');

// 对密码进行加密,生成随机字符串,默认是10,越高复杂度越高
const salt=await bcrypt.genSalt(10);

// 把加密后的密码赋值给password
const password=await bcrypt.hash(req.body.password,salt);

// 用加密后的密码对请求中的密码进行覆盖
req.body.password=password;

// 将用户信息添加到数据库中(引入User集合,使用create对用户进行创建)
await User.create(req.body);

 bycryptjs的compare加密比对  由于插入数据库中的时候的密码是被加密后的,

所以当用户登陆时还需要借助bcryptjs对用户输入的密码和数据库中的密码进行比对

const isValid=await bcrypt.compare(password,user.password);
// 参数1:用户输入的密码,参数2:数据库中的被加密的密码,返回值为布尔值

 登陆拦截

拦截1:还没有登陆的用户直接跳过登陆页面去访问其他地址(路由),重定向到登陆页面

拦截2:普通用户知道管理员的管理页面地址,登陆后修改请求地址访问管理页面

             重定向到普通用户的首页

// 登陆拦截
const guard= (req,res,next)=>{
	// 如果请求地址不是login并且用户名没有被定义
	if(req.url!='/login' && !req.session.username){
		res.redirect('/admin/login');
	}else{
		// 说明用户名已被定义,也就表明是登陆状态
		if(req.session.role=='normal'){
			// 如果是普通用户就阻止程序向下执行
			return res.redirect('/home/');
		}
		// 如果中间没有被拦截,就说明既登陆了也不是普通用户,那就是管理员,放行路由
		next();
	}
}

// 开放guard供引用本文件的地方调用
module.exports=guard;

拦截的调用(放在项目的入口文件中,匹配路由之前,一定要在匹配路由前做出拦截)

use匹配到一个路由后,若没有next将终止匹配,并做出响应,所以要放置匹配其他路由之前

// 在路径匹配之前做拦截,判断用户的登陆状态
// 如果还没有登陆就直接请求到用户列表页面,用户列表中请求的session的userInfo中的username将呈现没有被定义的状态,也就会报错
// 所以先来拦截路径,如果是登陆状态再放行,也就使用到了中间件,写到了中间件文件中,这里做了引用
app.use('/admin',require('./middleware/loginGuard'));

 用于 退出 的同步功能(删除session、清空模板中的用户信息、重定向到登陆页面)

module.exports=(req,res)=>{
	// 删除session
	req.session.destroy(function(){
		// 删除cookie
		res.clearCookie('connect.sid');
		// 清除模板中的用户信息
		req.app.locals.userInfo=null;
		// 重定向到用户登陆页面
		res.redirect('/admin/login');
	});
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值