node全棧項目及問題匯總

最近寫了一個node+mongo+vue的全棧項目,寫一個博客網(wǎng)站,該代碼node/vue都很注重模塊化處理, node中間件的使用,該代碼主要注重全棧前后端接口的調(diào)用及處理,前端的一些細節(jié)如驗證等并未寫,以下是整理出來遇到的問題及解決方案,歡迎多交流共同學習~
項目代碼: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根據(jù)id查詢/刪除
要使用objectID,封裝到findDocument,removeDocument等
詳情見1中代碼

3.static訪問靜態(tài)文件

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()
  }
}

前端發(fā)送請求的時候設置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過期,全局調(diào)用element組件,彈出彈框
引入vue,然后調(diào)用 Vue.prototype.$message.error(res.data.msg);
詳情看上面5中前端部分的代碼

7.axios攔截401,在js文件中進行路由router跳轉(zhuǎn)
router引入
new router()
Main.js中,也是把new router掛載載vue實例中,也就是this.$router
在其他文件中引入也是一樣,直接引入這個實例就可以調(diào)用router的方法,比如push
詳情看上面5的前端代碼

8.checkLogin中間間驗證token用戶是否登錄或者過期
驗證同時把username寫在req對象上,后面遇到發(fā)布文章或者之類的可以直接從req中拿到username

9.Node獲取不到req.body
通過Postman improve功能,復制瀏覽器的curl請求與postman對比,發(fā)現(xiàn)瀏覽器的請求頭為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 數(shù)據(jù)
  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請求是創(chuàng)建文章
delete請求是刪除文章
get請求是獲取文章列表
put請求是更新文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容