文件上传解决方案
multer模块的使用
这里我们要实现一个,文件上传的功能,实际上也非常的简单,我们使用一个multer的第三方组件来实现这一的一个功能
特别需要注意的事情:我们在做这个东西之前需要把这个东西
服务器的渲染
/router/admin/index.js
const multer = require(‘multer‘)
// 值得注意的地方;这个uploads文件夹的名字的U固定,我们的这个multer模块会自动的给你生成这样的一个文件夹
const upload = multer({ dest: __dirname + ‘/../../uploads‘ });
// 注意到了,我们上传文件的时候也需要token验证
app.post(‘/api/upload‘, upload.single(‘file‘), async(req, res) => {
const file = req.file;
//为了提供实时预览的功能,我们把这个文件的URL地址返回去就好了,注意啊这里你需要先配置cors还有static的处理
file.url = `http://localhost:3001/uploads/${file.filename}`
res.send(file)
})
这个就是我们实现文件上传的这样的一个操作
前台的渲染
这个东西我们还是要讲解一下的
注意我们的前台的上传操作有一个问题,就是我们的这个上传的文件的数据的名字一定是file,因为我们的在后台就定义了这个名字,前台要和这个名字一致
- Vue的解决方案
这里我们使用eleUI的东西,我们来看看这个东西
/demon.vue
<template>
<div>
<el-form-item label="头像">
<el-upload
class="avatar-uploader"
:action="uploadUrl"
:show-file-list="false"
:on-success="afterUpload">
<img v-if="model.avatar" :src="model.avatar" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
</div>
</template>
<script>
export default {
data(){
return {
model: {
avatar:‘‘
}
}
},
methods: {
async save(){
let res
//如果又id就意味着在执行接口
if (this.id) {
res = await this.$http.put(`rest/heroes/${this.id}`, this.model)
} else {
res = await this.$http.post(‘rest/heroes‘, this.model)
}
console.log(res);
this.$router.push(‘/heroes/list‘)
// 注意啊这个$message是eleUI的东西
this.$message({
type: ‘success‘,
message: ‘保存成功‘
})
},
},
}
</script>
- 原生的html还有ajax的解决方案
需要注意的东西就是这里的这个参数的配置
/viwe/test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="" method="post" id="forms">
<input type="file" name="file" id="feature">
<input type="submit" value="点击发送">
</form>
<img src="" alt="" id="imagesD">
</body>
<script src=‘./jquery-3.5.0.js‘></script>
<script>
// 当管理员选择文件的时候 触发事件
$(‘#feature‘).on(‘change‘, function() {
const file = this.files[0];
const formData = new FormData();
formData.append(‘file‘, file);
$.ajax({
type: ‘post‘,
url: ‘http://localhost:3001/api/upload‘,
data: formData,
processData: false,
contentType: false,
success: function(response) {
console.log(response.url)
$(‘#imagesD‘).attr({‘src‘:response.url});
}
})
});
</script>
</html>
以上就是我们的有关于上传文件的解决方案
用户注册加密登录解决方案
bcrypt
这里我们注册登录功能,主要就是使用bcrypt模块进行加密
注册
注册实际上就是增加数据的操作,只不过在存到数据库的中,提前进行加密就好了,我们的一个职业操守:“”
- 模型的操作
/model/AdminUser.js
const mongoose = require(‘mongoose‘)
const bcrypt = require(‘bcrypt‘)
const schema = new mongoose.Schema({
username: { type: String },
password: {
type: String,
select: false,//指定查询的时候不查询密码
// 这个是mongoose里面的一个构造函数
set(val) {
return bcrypt.hashSync(val, 10)
}
}
})
module.exports = mongoose.model(‘AdminUser‘, schema)
- 注册的操作不应改变就是一个原来的CRUD的增加的方法
就是下面这几个简单的方法
/router/admin/index.js
router.post(‘/api/articles‘, async(req, res) => {
const model = await Article.create(req.body)
// console.log(Article);
res.send(model)
})
以上就是我们的密码注册加密处理
登录
登录的逻辑也非常的简单,就是在调用的时候,传入参数进行查询就好了
/router/admin/index.js
app.post(‘/api/login‘, async(req, res) => {
const { username, password } = req.body
console.log(username, +‘‘ + password);
const user = await AdminUser.findOne({ username: username }).select(‘+password‘)
if(!user){
res.status(422).send({"message":‘user not finde"})
}
const isValid = require(‘bcrypt‘).compareSync(password, user.password)
if(!user){
res.status(422).send({"message":‘user not finde"})
}
res.status(200).send({"message":‘login Successful"})
})
邮箱验证解决方案
nodemailer模块
我们要求实现这样的逻辑,注册的时候,要求要进行邮箱验证,验证成功之后就允许注册,要不然就在注册发送请求的时候拦截掉,不允许调用这个接口
- 首先是配置nodemailer
/middleware/mail.js
const nodemailer = require(‘nodemailer‘);
//创建发送邮件的请求对象
let transporter = nodemailer.createTransport({
host: ‘smtp.qq.com‘, //发送端邮箱类型(QQ邮箱)
port: 465, //端口号
secure: true,
auth: {
user: ‘861795660@qq.com‘, // 发送方的邮箱地址(自己的)
pass: ‘dsumjmlrqjhdbcba‘ // mtp 验证码
}
});
function send(mail, code) {
let mailObj = {
from: ‘"来自BM老李的功能测试" <861795660@qq.com>‘, // 邮件名称和发件人邮箱地址
to: mail, //收件人邮箱地址(这里的mail是封装后方法的参数,代表收件人的邮箱地址)
subject: ‘验证信息‘, //邮件标题
text: ‘您的验证码是:‘ + code, // 邮件内容,这里的code是这个方法的参数,代表要发送的验证码信息,这里的内容可以自定义
}
// 发送邮件(封装成一个promise对象),方便后面调用该方法
//这部分代码直接复制粘贴即可,但是注意发送邮件的请求对象名称要和上面声明的名称保持一致(这里我的名称是transporter)
return new Promise((resolve, reject) => {
transporter.sendMail(mailObj, (err, data) => {
if (err) {
reject(err) //出错
} else {
resolve() //成功
}
})
})
}
module.exports = { send }
- 我们直接就调用这个send方法就能发送请去了
接下来看看我们的登录验证码的验证下发,验证进行,逻辑操作
- 注意一下,这里的这个getRandomNumber生成随机四位数的验证码是我们的自己封装的在utils下
代码逻辑非常的的简单
module.exports = {
getRandom(max, min) {
return Math.floor(Math.random() * (max - min)) + min;
}
}
- 进行邮箱验证
/router/admin/index.js
// 邮箱验证模块
let email_code = ‘‘
app.get(‘/api/getcode/:email‘, async(req, res) => {
const { send } = require(‘../../middleware/mail‘)
const { getRandom } = require(‘../../utils/getRandomNumber‘)
email_code = getRandom(1000, 9999)
await send(req.params.email, email_code)
res.status(200).send({ message: "验证码已经下发,请注意查收" })
})
// 进行邮箱验证
let isEmailVali = false
app.get(‘/api/autucode/:code‘, async(req, res) => {
if (req.params.code == email_code) {
isEmailVali = !isEmailVali
res.status(200).send({ message: "邮箱验证通过" })
} else {
isEmailVali = false
res.status(422).send({ message: "邮箱验证码错误" })
}
})
// 单一个的post不带参数就是表示----> 增 (往资源里面增加些什么)
router.post(‘/‘, async(req, res) => {
if (req.modelNmae == ‘AdminUser‘) {
if (isEmailVali) {
isEmailVali = false
email_code = ‘‘
const model = await req.Model.create(req.body)
res.send(model)
} else {
res.status(422).send({ message: "请填写正确的邮箱验证码" })
}
} else {
const model = await req.Model.create(req.body)
res.send(model)
}
})
JWT解决方案
jsonwebtoken
我们发现,目前我们是没有把用户的状态进行保存,什么意思呢?就是说,你(用户)虽然登录了,但是我(服务器)不知道你是不是登录的,因为
我没有办法,来判断 你这个用户是否是登录的,于是乎,我需要给你发一个令牌,用以验证你这个用户是否是登录的
- 你登录的时候我就给你下发签名之后的令牌(JWT)
- 首先需要说明的是,我们的一个验证的签名秘钥是定义在了我们的app实例上
/index.js
// 设置我们的token秘钥
app.set(‘secret‘, ‘132a1s3d1a31sd3a1ds31a3sd1a31ds‘)
/router/admin/index.js
app.post(‘/api/login‘, async(req, res) => {
const { username, password } = req.body
console.log(username, +‘‘ + password);
const user = await AdminUser.findOne({ username: username }).select(‘+password‘)
if(!user){
res.status(422).send({"message":‘user not finde"})
}
const isValid = require(‘bcrypt‘).compareSync(password, user.password)
if(!user){
res.status(422).send({"message":‘user not finde"})
}
const jwt = require(‘jsonwebtoken‘)
// 这样根据用户id进行的加密的签名的token就生成好了
const token = jwt.sign({id: user._id}, app.get(‘secret‘))
res.send({ token })
})
- 这个就是我们的验证登录的验证
/middelweare/auth.js
module.exports = options => {
const assert = require(‘http-assert‘)
const jwt = require(‘jsonwebtoken‘)
const AdminUser = require(‘../model/AdminUser‘)
return async(req, res, next) => {
const token = String(req.headers.authorization || ‘‘).split(‘ ‘).pop()
if(!token){
res.status(422).send({"message":‘请先登录"})
}
// 进行token的解密
const { id } = jwt.verify(token, req.app.get(‘secret‘))
if(!token){
res.status(422).send({"message":‘请先登录"})
}
req.user = await AdminUser.findById(id)
if(!token){
res.status(422).send({"message":‘请先登录"})
}
await next()
}
}
- 前端处理
注意一点:由于我们要进行验证,所以你的请求头中就应该附带上我们的token
这里我们使用axios进行拦截,保存处理
/view/tokenTest.html
+++++
const http = axios.create({
baseURL: ‘http://localhost:3000/admin/api‘
})
// 添加请求拦截器
http.interceptors.request.use(function(config) {
// 在发送请求之前做些什么,注意这个Bases是行业规范,注意要加空格,我们再发送的时候把token加到每一个请求中
if(localStorage.token){
config.headers.Authorization = ‘Bearer ‘ + (localStorage.token || ‘‘)
}
return config
}, function(error) {
// 对请求错误做些什么
return Promise.reject(error);
});
统一的错误处理中间件
http-asset模块
我们发现,我们的程序中,有很多的的的地方都有可能报错,现在我们有这样的一个需求:要求统一处理这些错误,并且统一用一种格式进行抛出错误
- 封装函数
我们封装的这个函数,它需要完成的一个功能就是,抛出一个异常出去,并且调用next 跳出去,不执行后续的代码,为此我们需要使用http-assert模块的assert方法,来实现这样的一个功能
/utlis/assert.js
module.exports = {
assert(value, status, msg, next) {
const asserts = require(‘http-assert‘)
try {
asserts(value, status, msg)
} catch (error) {
next(error)
}
}
}
- 接下来,你要做的事情,就是把所有的ifxxxbolean值有可能报错的地方都去替换这个东西就好了
/router/admin/index.js
const { assert } = require(‘../../utils/assert‘)
++++
app.use(‘/api/login‘, async(req, res, next) => {
const { username, password } = req.body
const user = await AdminUser.findOne({ username: username }).select(‘+password‘)
console.log(‘用户是:‘ + user);
assert(user, 422, ‘用户不存在‘, next)
// if (!user) {
// return res.status(422).send({ message: ‘用户不存在‘ })
// }
const isValid = require(‘bcrypt‘).compareSync(password, user.password)
assert(isValid, 422, ‘密码错误‘, next)
const token = jwt.sign({
id: user._id
}, app.get(‘secret‘))
res.send({ token })
})
- 统一的处理错误,错误处理中间件
如果你在这里不这样做,那么你是发送不去错误处理信息的,这里才是统一的处理asset函数抛出来的错误,处理之后的逻辑代码如下
/router/admin/index.js
// 错误处理中间件,所有的错误都被抛出到这里来了,需要注意的是 我们的这req.statusCode是http-asster中间件处理之后得到的
// 错误处理中间件,统一的处理我们http-assart抛出的错误
app.use(async(err, req, res, next) => {
res.status(err.statusCode || 500).send({
message: err.message
})
})