Node中的一些概念和中間件的介紹

一、搭建基本服務(wù)器

  1. 引入兩個模塊:http、fs
  2. 創(chuàng)建http對象,監(jiān)聽端口
  3. 引入url、querystring模塊
  4. 處理靜態(tài)文件請求、接收動態(tài)數(shù)據(jù)

二、模塊化開發(fā)

  1. 模塊化的意義:形成局部作用域,不會污染全局變量

    • commonJS:node、webpack是其規(guī)范的實(shí)現(xiàn)
    • node不支持ES6的模塊化,但支持所有的ES6+語法
    • 可以通過typescript轉(zhuǎn)化,在node中使用ES6模塊化
  2. 批量導(dǎo)出可輸出多次

    • exports.屬性1 = 值1
    • exports.屬性2 = 值2
    • 導(dǎo)出的都是屬性,可導(dǎo)出任何類型的值
    • 但導(dǎo)入的只是對象,通過對象的屬性執(zhí)行
  3. 默認(rèn)導(dǎo)出只輸出一次

    • 默認(rèn)導(dǎo)出只輸出第一個值
    • module.exports = [a,b]
    • module.exports = {a,b}
    • 當(dāng)批量導(dǎo)出和默認(rèn)導(dǎo)出同時存在,只輸出默認(rèn)導(dǎo)出
    • 且下面的默認(rèn)輸出會覆蓋上面的默認(rèn)輸出語句
    • 可以導(dǎo)出任何類型,導(dǎo)出什么類型,引入的就是什么類型
  4. 引入的類型跟輸出形式有關(guān)

    • 批量導(dǎo)出,引入的都是對象
      • 引入對象:const module = require("路徑")
      • 按需使用,引入對象身上的屬性
        • const module = require("路徑").屬性
    • 默認(rèn)導(dǎo)出,與引入類型相同
      • const module = require("路徑")[i]
      • const module = require("路徑").屬性
    • 沒有導(dǎo)出,引入的就是空對象
    • 引入路徑:支持任何類型
      • 不指定路徑:先找系統(tǒng)模塊 -> 再從項(xiàng)目環(huán)境找node_modules|bower_components(依賴模塊) -> not found
      • 指定路徑:找指定路徑 -> not found
  5. 模塊化代碼執(zhí)行

    • 模塊里的代碼從引入的那一行開始執(zhí)行
    • 導(dǎo)出的值從引入后調(diào)用的那一行開始執(zhí)行

三、express

  1. 包管理工具:npm、yarn、bower

  2. 接口響應(yīng)

    • 支持各種請求方式:get、post、put、delete...
      app.請求姿勢API(接口名稱,處理函數(shù))
      app.get(url,(req,res,next)=>{})
      app.post(url,(req,res,next)=>{})
      ...
    
  3. app.use():傳入中間件到app實(shí)例

    • 安裝中間件、路由,接受一個函數(shù)
    • use響應(yīng)所有的請求姿勢(get,post,...)
      // app.use([地址],中間件|路由|函數(shù)體)
      // 地址 "/" 可省略
      app.listen("3000","主機(jī)",()=>{});
      app.use(express.static("./www"));
      app.use(bodyParser());
    
  4. app.all():處理子管道的共同業(yè)務(wù)

      app.all("/admin/*",(req,res,next)=>{
        next() // 管道流,流入下一管道
      })
      // all匹配全路徑 處理所有HTTP 
      // 需要next()延續(xù)后續(xù)
    
  5. 動態(tài)接口:admin/:ab/:abc

    • 響應(yīng)動態(tài)url接口地址
    • ~/admin/abc/dadc
    • ~/admin/s12/acs33
  6. 請求體/request

      req.query  // 獲取地址欄的數(shù)據(jù)
      req.body   // 獲取非地址欄的數(shù)據(jù)  依賴中間件 
      // req.body依賴中間件:body-parser
      req.params // 獲取動態(tài)接口名
      req.method // 獲取前端提交方式
    
  7. 響應(yīng)體/response

      res.send(any) // 對等 res.write + end
      res.end(string|buffer)
      res.json(json) // 返回json
      res.status(404).send() // 返回狀態(tài)和信息
      res.jsonp(響應(yīng)數(shù)據(jù)) // 調(diào)用請求時的回調(diào)函數(shù)并傳遞響應(yīng)數(shù)據(jù)
      res.sendFile(path.resolve('public/error.html')) // 渲染純 HTML 文件
      // 上部引入const path = require("path");
    
  8. jsonp響應(yīng)

      app.set('jsonp callback name','回調(diào)函數(shù)名') // 默認(rèn)callback
      app.get('/jsonp接口',(req,res,next)=>res.jsonp(數(shù)據(jù)))
    
  9. 中間件

    • middleware,處理自定義業(yè)務(wù),只處理請求到結(jié)束響應(yīng)的中間部分
      // npm i body-parser -S // 安裝包
      let bodyParser=require('body-parser') // 引入中間件
      app.use(bodyParser()) // 安裝中間件
    
  10. 后端跳轉(zhuǎn)

      app.get("/api/old", (req, res, next) => {
        res.redirect("/api/new");
      })
      // res.redirect(url) // 指向一個接口
      app.get("/api/new", (req, res) => {
        console.log("這是新業(yè)務(wù)");
      })
    

四、身份驗(yàn)證

(一)session

  1. 客戶端用戶名跟密碼請求登錄
  2. 服務(wù)端收到請求,去庫驗(yàn)證用戶名與密碼
  3. 驗(yàn)證成功后,服務(wù)端種一個cookie或發(fā)一個字符到客戶端,同時服務(wù)器保留一份session
  4. 客戶端收到 響應(yīng) 以后可以把收到的字符存到cookie
  5. 客戶端每次向服務(wù)端請求資源的cookie會自動攜帶
  6. 服務(wù)端收到請求,然后去驗(yàn)證cookie和session,如果驗(yàn)證成功就向客戶端返回請求的庫數(shù)據(jù)

Session存儲位置:服務(wù)器內(nèi)存,磁盤,或者數(shù)據(jù)庫里
Session存儲內(nèi)容:id,存儲時間,用戶名等說明一下登錄的用戶是誰
客戶端攜帶:cookie自動帶,localStorage手動帶

如何保存信息給瀏覽器

  • 前端種:
    • cookie/localstorage
  • 后端種:
    1. 服務(wù)器給瀏覽器種cookie: cookie-parser,只種cookie,不留session
    2. 服務(wù)器給瀏覽器種cookie的同時在服務(wù)器上生成seesion: cookie-session

cookie-session

  // 安裝并引入cookie-session
  const cookieSession = require('cookie-session');

  // 配置中間件
  app.use(cookieSession({
    name: "test_session", // 保存到服務(wù)器的session的名字
    keys: ["a", "b", "c"], // [必傳參數(shù),代表加密層級]
    maxAge:1000 //保留cookie的時間,ms
  }));

  // 種cookie,備份session
  req.session.key=value;

  // 刪除cokkie、session
  delete req.session.key;
  req.session.key = undefined;

(二)token

  • 在服務(wù)端不需要存儲用戶的登錄記錄,全部發(fā)給客戶端有客戶端自己存(cookie,local)
  1. 客戶端使用用戶名跟密碼請求登錄
  2. 服務(wù)端收到請求,去驗(yàn)證用戶名與密碼
  3. 驗(yàn)證成功后,服務(wù)端會簽發(fā)一個 Token(加了密的字符串),再把這個 Token 發(fā)送給客戶端
  4. 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
  5. 客戶端每次向服務(wù)端請求資源的時候需要帶著服務(wù)端簽發(fā)的 Token
  6. 服務(wù)端收到請求,然后去驗(yàn)證客戶端請求里面帶著的 Token,如果驗(yàn)證成功,就向客戶端返回請求的數(shù)據(jù)

token的實(shí)現(xiàn)

  // 安裝并引入jsonwebtoken
  const jwt = require('jsonwebtoken');

  // 生成token,返回給客戶端 --- 異步回調(diào)函數(shù)
  // jwt.sign({username,id:"db_id"},"test_token",(err,token)=>{
  //   if(!err) res.send({err:0,msg:"登錄成功",data:[],token});
  // });
  // 生成token,同步獲取
  let token = jwt.sign({username,id:"db_id"},"test_token");
  res.send({err:0,msg:"登錄成功",data:[],token});

  // 獲取客戶端發(fā)送的token
  let token = req.headers.token || req.query.token || req.body.token;
  console.log(token);

  // 校驗(yàn)token
  jwt.verify(token,"test_token",(err,decode)=>{
    if(err){
      res.send({err:2,msg:"token校驗(yàn)失敗",data:"no data"});
    }else{
      res.send({err:1,msg:"token校驗(yàn)成功",data:[]});
    }
  });

  // 注銷,刪除token前端完成

(三)兩者區(qū)別

  1. session需要在用戶端保存信息;
  2. token能夠避免CSRF攻擊;
  3. token的安全性更高;
  4. session存在多服務(wù)器粘性問題。

五、文件上傳

  1. 思想:前端表單->后端接收到文件本身->保存到服務(wù)器上->給數(shù)據(jù)庫記錄文件一些信息->庫返回給nodejs相關(guān)信息->nodejs返回給前端

      <!-- 前端 -->
      <input type=file enctype="multipart/form-data" name="fieldname">
    
  2. 實(shí)現(xiàn):multer->文件名會隨機(jī)->fs模塊改名->path系統(tǒng)模塊解析磁盤路徑

    • 后端:multer 接受 form-data編碼數(shù)據(jù)

(一)path模塊

  1. 操作系統(tǒng)磁盤路徑

  2. 編碼

    • windows:c:\\user\\admin\\a.jpg
    • mac:~/desktop/1901
  3. UI呈現(xiàn)

    • windows: c:\user\admin
    • mac: ~/desktop/1901
  4. API

    • 磁盤路徑解析 parse

        // string -> object
        // path.parse('c:\\wamp\\xx.png')
        path.parse('./wamp/xx.png')
        //返回
        {
          root: 'c:\\', 盤符
          dir: 'c:\\wamp', 目錄
          base: 'xx.png',  文件名
          ext: '.png', 擴(kuò)展名
          name: 'xx'    文件,不含擴(kuò)展名
        }
      
    • 片段合并 join

      • path.join('磁盤路徑1','磁盤路徑2','磁盤路徑n')
      • __dirname 全局|魔術(shù)變量 返回當(dāng)前文件所在的磁盤路徑
    • 片段合并 resolve

      • path.resolve('磁盤路徑1','磁盤路徑n')
      • 合并磁盤片段,從右到左找根,找到從當(dāng)前向右拼接,沒有找到根,以當(dāng)前文件路徑為根

(二)multer中間件

  1. multer 接受 form-data編碼數(shù)據(jù),所有要求前端攜帶時應(yīng)注意

    • 如:<input type=file enctype="multipart/form-data" name="icon">
  2. 使用

      //1 引入
      let multer  = require('multer');
      //2 實(shí)例化  
      let objMulter = multer({ dest: './upload' }); //dest: 指定 保存位置(存到服務(wù)器)
      //安裝中間件
      app.use(objMulter.any());  //允許上傳什么類型文件,any 代表任何類型 
    
  3. 中間件擴(kuò)展了req請求體 req.files

      app.get('/reg',(req,res)=>{
        req.files // 多個文件
        // req.file // 單個文件
      })
    
      fieldname: 表單name名
      originalname: 上傳的文件名
      encoding: 編碼方式
      mimetype: 文件類型
      buffer: 文件本身
      size:尺寸
      destination: 保存路徑
      filename: 保存后的文件名  不含后綴
      path: 保存磁盤路徑+保存后的文件名 不含后綴
    

六、后端渲染

  1. 通常根據(jù)后端返回的json數(shù)據(jù),然后來生成html被稱為前端渲染,而后端渲染是后端把json與html結(jié)合渲染好后返回到瀏覽器,沒前端什么事了

  2. 模板引擎

    • 無論前后誰來渲染頁面,都會用到模板引擎,前端渲染頁面實(shí)際上是操作dom,后端渲染頁面是把數(shù)據(jù)和html字符拼接后丟給瀏覽器

(一)jade

  1. 使用

      let jade = require('jade')
      let html = jade.renderFile('jade模板文件',{數(shù)據(jù)},{pretty:true});    //返回字符
    
  2. jade語法

    • 父子要縮進(jìn)
    • 屬性:標(biāo)簽(key=value,key2=value)
    • 內(nèi)容: 標(biāo)簽 內(nèi)容
  3. 其他擴(kuò)展

(二)ejs

  1. 使用

      let ejs = require('ejs')
      ejs.renderFile('ejs模板文件',{要合并到html數(shù)據(jù)},回調(diào)(err,data))
      // err:錯誤,null代表沒有錯誤
      // data:渲染后的字符|流  
      // ejs模板:后綴名為ejs的html文件
    
  2. ejs語法

    • ejs 結(jié)構(gòu)就是html
    • 輸出: <%= 數(shù)據(jù)名|屬性名|變量名 + 表達(dá)式 %>
    • 語句: <% 語句 %> 需要被<% %> 包裹
    • 非轉(zhuǎn)義輸出: <%- 數(shù)據(jù)名|變量名 + 表達(dá)式 %>
    • 載入公共:<%- include('./hd.ejs',{數(shù)據(jù)}) %>
  3. 其他擴(kuò)展

七、路由

  1. 告訴你去哪,對于前端,主要是導(dǎo)向,告訴瀏覽器應(yīng)該去哪,對于后端,可以理解為一個子服務(wù),一個路由就是一個小的服務(wù)(server/app)模塊,處理一個接口

  2. 配置和使用

    • 創(chuàng)建模塊文件:/router/xx.js
        // 1.創(chuàng)建路由
        let router = express.Router(); 
      
        // 2.路由處理響應(yīng)
        router.響應(yīng)API(地址, 處理函數(shù));
      
        // 3.導(dǎo)出路由
        module.exports = router;
      
    • 主服務(wù):/app.js
        //安裝路由
        app.use('地址',router); 
      
    • 子路由/子服務(wù)/子模塊:/router/xx.js
        //子路由里安裝路由 嵌套
        router.use('地址',子router) 
      
        //截獲當(dāng)前路由下的部分公共業(yè)務(wù)
        router.all('*',當(dāng)前router路由下的驗(yàn)證工作) //需要next 延續(xù)
      
    • 主路由的地址對應(yīng)子路由的根
      • 如:app.js: /api/user ~~ user.js: /
      • 如:app.js: /api/user/add ~~ user.js: /add

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容