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請求是更新文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容