NodeJS——大汇总(二)(只需要使用这些东西,就能处理80%以上业务需求,全网最全node解决方案,吐血整理)

文件上传解决方案

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,因为我们的在后台就定义了这个名字,前台要和这个名字一致

  1. 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>



  1. 原生的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模块进行加密

注册

注册实际上就是增加数据的操作,只不过在存到数据库的中,提前进行加密就好了,我们的一个职业操守:“”

  1. 模型的操作
    /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)

  1. 注册的操作不应改变就是一个原来的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模块

我们要求实现这样的逻辑,注册的时候,要求要进行邮箱验证,验证成功之后就允许注册,要不然就在注册发送请求的时候拦截掉,不允许调用这个接口

  1. 首先是配置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 }

  1. 我们直接就调用这个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

我们发现,目前我们是没有把用户的状态进行保存,什么意思呢?就是说,你(用户)虽然登录了,但是我(服务器)不知道你是不是登录的,因为
我没有办法,来判断 你这个用户是否是登录的,于是乎,我需要给你发一个令牌,用以验证你这个用户是否是登录的

  1. 你登录的时候我就给你下发签名之后的令牌(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 })
    })
  
  1. 这个就是我们的验证登录的验证
    /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()
    }
}
  1. 前端处理

注意一点:由于我们要进行验证,所以你的请求头中就应该附带上我们的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模块

我们发现,我们的程序中,有很多的的的地方都有可能报错,现在我们有这样的一个需求:要求统一处理这些错误,并且统一用一种格式进行抛出错误

  1. 封装函数

我们封装的这个函数,它需要完成的一个功能就是,抛出一个异常出去,并且调用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 })
    })
  1. 统一的处理错误,错误处理中间件

如果你在这里不这样做,那么你是发送不去错误处理信息的,这里才是统一的处理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
        })
    })

项目GIT地址

https://github.com/BM-laoli/UniversalPackforNpm

原文地址:https://www.cnblogs.com/BM-laoli/p/12976696.html