zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    A1047 Student List for Course [unordered_map]
    .net 事务处理的三种方法
    SQline安装
    LeetCode 21 _ 合并两个有序链表
    LeetCode 191 _ 位1的个数
    LeetCode 268 _ 缺失数字
    LeetCode 190 _ 位运算
    LeetCode 136 _ 位运算
    LeetCode 461 _ 位运算
    LeetCode 125 _ 字符串
  • 原文地址:https://www.cnblogs.com/BM-laoli/p/12976696.html
Copyright © 2011-2022 走看看