一、搭建基本服務(wù)器
- 引入兩個模塊:http、fs
- 創(chuàng)建http對象,監(jiān)聽端口
- 引入url、querystring模塊
- 處理靜態(tài)文件請求、接收動態(tài)數(shù)據(jù)
二、模塊化開發(fā)
-
模塊化的意義:形成局部作用域,不會污染全局變量
- commonJS:node、webpack是其規(guī)范的實(shí)現(xiàn)
- node不支持ES6的模塊化,但支持所有的ES6+語法
- 可以通過typescript轉(zhuǎn)化,在node中使用ES6模塊化
-
批量導(dǎo)出可輸出多次
exports.屬性1 = 值1exports.屬性2 = 值2- 導(dǎo)出的都是屬性,可導(dǎo)出任何類型的值
- 但導(dǎo)入的只是對象,通過對象的屬性執(zhí)行
-
默認(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)出什么類型,引入的就是什么類型
-
引入的類型跟輸出形式有關(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
- 批量導(dǎo)出,引入的都是對象
-
模塊化代碼執(zhí)行
- 模塊里的代碼從引入的那一行開始執(zhí)行
- 導(dǎo)出的值從引入后調(diào)用的那一行開始執(zhí)行
三、express
包管理工具:npm、yarn、bower
-
接口響應(yīng)
- 支持各種請求方式:get、post、put、delete...
app.請求姿勢API(接口名稱,處理函數(shù)) app.get(url,(req,res,next)=>{}) app.post(url,(req,res,next)=>{}) ... -
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()); -
app.all():處理子管道的共同業(yè)務(wù)
app.all("/admin/*",(req,res,next)=>{ next() // 管道流,流入下一管道 }) // all匹配全路徑 處理所有HTTP // 需要next()延續(xù)后續(xù) -
動態(tài)接口:admin/:ab/:abc
- 響應(yīng)動態(tài)url接口地址
~/admin/abc/dadc~/admin/s12/acs33
-
請求體/request
req.query // 獲取地址欄的數(shù)據(jù) req.body // 獲取非地址欄的數(shù)據(jù) 依賴中間件 // req.body依賴中間件:body-parser req.params // 獲取動態(tài)接口名 req.method // 獲取前端提交方式 -
響應(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"); -
jsonp響應(yīng)
app.set('jsonp callback name','回調(diào)函數(shù)名') // 默認(rèn)callback app.get('/jsonp接口',(req,res,next)=>res.jsonp(數(shù)據(jù))) -
中間件
- middleware,處理自定義業(yè)務(wù),只處理請求到結(jié)束響應(yīng)的中間部分
// npm i body-parser -S // 安裝包 let bodyParser=require('body-parser') // 引入中間件 app.use(bodyParser()) // 安裝中間件 -
后端跳轉(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
- 客戶端用戶名跟密碼請求登錄
- 服務(wù)端收到請求,去庫驗(yàn)證用戶名與密碼
- 驗(yàn)證成功后,服務(wù)端種一個cookie或發(fā)一個字符到客戶端,同時服務(wù)器保留一份session
- 客戶端收到 響應(yīng) 以后可以把收到的字符存到cookie
- 客戶端每次向服務(wù)端請求資源的cookie會自動攜帶
- 服務(wù)端收到請求,然后去驗(yàn)證cookie和session,如果驗(yàn)證成功就向客戶端返回請求的庫數(shù)據(jù)
Session存儲位置:服務(wù)器內(nèi)存,磁盤,或者數(shù)據(jù)庫里
Session存儲內(nèi)容:id,存儲時間,用戶名等說明一下登錄的用戶是誰
客戶端攜帶:cookie自動帶,localStorage手動帶
如何保存信息給瀏覽器
- 前端種:
- cookie/localstorage
- 后端種:
- 服務(wù)器給瀏覽器種cookie: cookie-parser,只種cookie,不留session
- 服務(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)
- 客戶端使用用戶名跟密碼請求登錄
- 服務(wù)端收到請求,去驗(yàn)證用戶名與密碼
- 驗(yàn)證成功后,服務(wù)端會簽發(fā)一個 Token(加了密的字符串),再把這個 Token 發(fā)送給客戶端
- 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
- 客戶端每次向服務(wù)端請求資源的時候需要帶著服務(wù)端簽發(fā)的 Token
- 服務(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ū)別
- session需要在用戶端保存信息;
- token能夠避免CSRF攻擊;
- token的安全性更高;
- session存在多服務(wù)器粘性問題。
五、文件上傳
-
思想:前端表單->后端接收到文件本身->保存到服務(wù)器上->給數(shù)據(jù)庫記錄文件一些信息->庫返回給nodejs相關(guān)信息->nodejs返回給前端
<!-- 前端 --> <input type=file enctype="multipart/form-data" name="fieldname"> -
實(shí)現(xiàn):multer->文件名會隨機(jī)->fs模塊改名->path系統(tǒng)模塊解析磁盤路徑
- 后端:multer 接受 form-data編碼數(shù)據(jù)
(一)path模塊
操作系統(tǒng)磁盤路徑
-
編碼
- windows:
c:\\user\\admin\\a.jpg - mac:
~/desktop/1901
- windows:
-
UI呈現(xiàn)
- windows:
c:\user\admin - mac:
~/desktop/1901
- windows:
-
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中間件
-
multer 接受 form-data編碼數(shù)據(jù),所有要求前端攜帶時應(yīng)注意
- 如:
<input type=file enctype="multipart/form-data" name="icon">
- 如:
-
使用
//1 引入 let multer = require('multer'); //2 實(shí)例化 let objMulter = multer({ dest: './upload' }); //dest: 指定 保存位置(存到服務(wù)器) //安裝中間件 app.use(objMulter.any()); //允許上傳什么類型文件,any 代表任何類型 -
中間件擴(kuò)展了req請求體
req.filesapp.get('/reg',(req,res)=>{ req.files // 多個文件 // req.file // 單個文件 })fieldname: 表單name名 originalname: 上傳的文件名 encoding: 編碼方式 mimetype: 文件類型 buffer: 文件本身 size:尺寸 destination: 保存路徑 filename: 保存后的文件名 不含后綴 path: 保存磁盤路徑+保存后的文件名 不含后綴
六、后端渲染
通常根據(jù)后端返回的json數(shù)據(jù),然后來生成html被稱為前端渲染,而后端渲染是后端把json與html結(jié)合渲染好后返回到瀏覽器,沒前端什么事了
-
模板引擎
- 無論前后誰來渲染頁面,都會用到模板引擎,前端渲染頁面實(shí)際上是操作dom,后端渲染頁面是把數(shù)據(jù)和html字符拼接后丟給瀏覽器
(一)jade
-
使用
let jade = require('jade') let html = jade.renderFile('jade模板文件',{數(shù)據(jù)},{pretty:true}); //返回字符 -
jade語法
- 父子要縮進(jìn)
- 屬性:標(biāo)簽(key=value,key2=value)
- 內(nèi)容: 標(biāo)簽 內(nèi)容
(二)ejs
-
使用
let ejs = require('ejs') ejs.renderFile('ejs模板文件',{要合并到html數(shù)據(jù)},回調(diào)(err,data)) // err:錯誤,null代表沒有錯誤 // data:渲染后的字符|流 // ejs模板:后綴名為ejs的html文件 -
ejs語法
- ejs 結(jié)構(gòu)就是html
- 輸出: <%= 數(shù)據(jù)名|屬性名|變量名 + 表達(dá)式 %>
- 語句: <% 語句 %> 需要被<% %> 包裹
- 非轉(zhuǎn)義輸出: <%- 數(shù)據(jù)名|變量名 + 表達(dá)式 %>
- 載入公共:<%- include('./hd.ejs',{數(shù)據(jù)}) %>
七、路由
告訴你去哪,對于前端,主要是導(dǎo)向,告訴瀏覽器應(yīng)該去哪,對于后端,可以理解為一個子服務(wù),一個路由就是一個小的服務(wù)(server/app)模塊,處理一個接口
-
配置和使用
- 創(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
- 如:app.js:
- 創(chuàng)建模塊文件: