node全栈项目及问题汇总

最近写了一个node+mongo+vue的全栈项目,写一个博客网站,该代码node/vue都很注重模块化处理, node中间件的使用,该代码主要注重全栈前后端接口的调用及处理,前端的一些细节如验证等并未写,以下是整理出来遇到的问题及解决方案,欢迎多交流共同学习~
项目代码:https://github.com/Little-God/node-vue-blog
1.mongodb模块
把mongodb的增删改查操作都封装成模块方法

const assert = require('assert');
const ObjectID = require('mongodb').ObjectID;
const insertDocuments = function(db, documents, query, callback) {
  // Get the documents collection
  const collection = db.collection(documents);
  // Insert some documents
  collection.insertMany([query], function(err, result) {
    assert.equal(err, null);
    callback(result);
  });
}

const findDocuments = function(db, documents, query, callback) {
  // Get the documents collection
  const collection = db.collection(documents);
  // Find some documents
  if(query._id) query._id = ObjectID(query._id)
  collection.find(query).toArray(function(err, docs) {
    assert.equal(err, null);
    callback(docs);
  });
}

const updateDocument = function(db, documents, query, setQuery,  callback) {
  // Get the documents collection
  const collection = db.collection(documents);
  // Update document where a is 2, set b equal to 1
  collection.updateOne(query, setQuery, function(err, result) {
      assert.equal(err, null);
      callback(result);
    });
}

const removeDocument = function(db, documents, query, callback) {
  // Get the documents collection
  const collection = db.collection(documents);
  // Delete document where a is 3
  if(query._id) query._id = ObjectID(query._id)
  collection.deleteOne(query, function(err, result) {
    assert.equal(err, null);
    callback(result);
  });
}

const indexCollection = function(db, documents, query, callback) {
  db.collection(documents).createIndex(
    query,
    null,
    function(err, results) {
      console.log(results);
      callback();
    }
  );
};

module.exports = {
  insertDocuments,
  findDocuments,
  updateDocument,
  removeDocument,
  indexCollection
}


2.mongodb根据id查询/删除
要使用objectID,封装到findDocument,removeDocument等
详情见1中代码

3.static访问静态文件

app.use(express.static(path.join(__dirname, 'static')))

4.mode解决跨域问题
使用cors模块,要先npm i cors -S

const cors = require('cors')
app.use(cors())

5.用户登录验证token
用户输入用户名和密码登录时后端返回一个token,前端存在localStorage, 请求接口的时候把localStorage中的token在axios设置header,后段验证,如果过期,则返回401,重新登录
jsonwebtokens模块用来生成token,生成出来的token会包含过期时间
写一个checkLogin中间件,来验证token是否过期

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const config = require('../config/default')
const mongo = require('../lib/mongo')
const jwt = require('jsonwebtoken');

module.exports = {
  // 验证token是否过期
  checkLogin: function checkLogin(req, res, next) {
    if (req.headers.token) {
      const token = req.headers.token
      console.log(token)
      MongoClient.connect(config.mongodb, function (err, client) {
        assert.equal(null, err);
        const db = client.db(config.dbName);
        mongo.findDocuments(db, 'token', {token:token}, function (data) {
          if(data.length>0){
            // invalid token
            jwt.verify(token, 'ken', function(err, decoded) {
              if(err) {
                return res.json({
                  code:401,
                  msg:'token过期,请重新登录~'
                })
              }
              req.username = decoded.name
              client.close();
              next()
            });
          } else {
            client.close();
            return res.json({
              code:401,
              msg:'宁还未登录,请登录~'
            })
          }
        });
      });
    } else {
      return res.json({
        code:401,
        esg:'token过期,请重新登录~'
      })
    }
  },

  checkNotLogin: function checkNotLogin(req, res, next) {
    if (req.session.user) {
      req.flash('error', '已登录')
      return res.redirect('back')// 返回之前的页面
    }
    next()
  }
}

前端发送请求的时候设置token请求头,并且拦截响应,看token是否有过期。

import http from 'axios'
import qs from 'querystring';
import router from '@/router'
import Vue from 'vue'


const axios = http.create({
  baseURL: 'http://127.0.0.1:3000',
  timeout: 1000,
  headers: {'Content-Type': 'application/json'}
});

axios.interceptors.response.use((res) => {
  if (res.data.code && res.data.code === 401) { // 401, token失效
    Vue.prototype.$message.error(res.data.msg);
    setTimeout(()=> {
      router.push('/signin')
    }, 500)
  }
  return res
}, (err) => {
  return Promise.reject(err)
})

axios.interceptors.request.use((config) => {
  config.headers['token'] = window.localStorage.getItem('token') || ''
  return config
}, error => {
  return Promise.reject(error)
})

6.js文件中使用element组件
401过期,全局调用element组件,弹出弹框
引入vue,然后调用 Vue.prototype.$message.error(res.data.msg);
详情看上面5中前端部分的代码

7.axios拦截401,在js文件中进行路由router跳转
router引入
new router()
Main.js中,也是把new router挂载载vue实例中,也就是this.$router
在其他文件中引入也是一样,直接引入这个实例就可以调用router的方法,比如push
详情看上面5的前端代码

8.checkLogin中间间验证token用户是否登录或者过期
验证同时把username写在req对象上,后面遇到发布文章或者之类的可以直接从req中拿到username

9.Node获取不到req.body
通过Postman improve功能,复制浏览器的curl请求与postman对比,发现浏览器的请求头为content-type:application-json;utf-8; application-json,
实际上要content-type:application-json,不能有多余的字符才可请求成功
解决:
修改axios源码
-axios
--lib
---defaults.js
defaults.js文件里transformRequest方法中,为post请求时,把默认加上的content-type的值去掉,自己在引入axios全局设置post的请求头

  1. formidable模块,node上传图片等文件接口要使用到的
    注意:上传后文件名会改,并且会去掉后缀名,所以在存储的时候要用返回域名+储存后的地址的文件名
const express = require('express')
const router = express.Router()
const formidable = require('formidable');
const path = require('path');


// POST /upload 上传图片
router.post('/',  function (req, res, next) {
  let form = new formidable.IncomingForm();
  form.encoding = 'utf-8'; // 编码
  // 保留扩展名
  form.keepExtensions = true;
  //文件存储路径 最后要注意加 '/' 否则会被存在public下
  form.uploadDir = path.join(__dirname, '../static/');
  // 解析 formData 数据
  form.parse(req, (err, fields ,files) => {
    if(err) return next(err)
    let imgPath = files.file.path;
    let imgName = files.file.name;
    console.log(imgName, imgPath);
    // 返回路径和文件名
    res.json({code: 1, data: { name: imgName, path: 'http://127.0.0.1:3000/'+imgPath.split('/')[imgPath.split('/').length-1] }});
  })
})

module.exports = router

11.注意查表的时候如果值为空对象{}那么查出来的是全部文档

12.restfull设计接口
通过url设计请求方法定义资源的操作
如:同一个接口/posts
post请求是创建文章
delete请求是删除文章
get请求是获取文章列表
put请求是更新文章

你可能感兴趣的:(node全栈项目及问题汇总)