柚子快報(bào)激活碼778899分享:Node.js
柚子快報(bào)激活碼778899分享:Node.js
Node.js
NOde.js是一個(gè)基于Chrome V8引擎的JavaScript運(yùn)行環(huán)境
瀏覽器是前端運(yùn)行環(huán)境Node.js是后端運(yùn)行環(huán)境Node.js無法調(diào)用dom
1.簡單模塊
1.1 fs 文件系統(tǒng)模塊
1.1.1讀取
fs.readFile()方法 用來讀取指定文件中的內(nèi)容fs.writeFile()方法 用來向指定的文件中寫入內(nèi)容
// 導(dǎo)入 fs模塊
const fs = require('fs')
// 調(diào)用 fs.readFile()方法讀取文件
// 參數(shù)1 讀取文件的存放路徑
// 參數(shù)2 讀取文件時(shí)候采用的編碼格式 一般utf-8
fs.readFile('./day01/1.txt','utf-8',function(err,dataStr){
//打印失敗的結(jié)果
console.log(err)
console.log('---------')
//打印成功的結(jié)果
console.log(dataStr)
})
判斷文件是否讀取成功
err 對象為null 讀取成功 (err和dataStr在此處只是一個(gè)名字可自定義)err對象為錯(cuò)誤對象 dataStr的值為underfined
1.1.2寫入
fs.writeFile('./day01/1.txt','abcd',function(err){
console.log(err)
})
err 對象為null 寫入成功
1.1.3成績寫入案例
const fs = require('fs')
fs.readFile('./day01/成績.txt', 'utf8', function(err, dataStr) {
if (err) {
return console.log('讀取文件失??!' + err.message)
}
// 4.1 先把成績的數(shù)據(jù),按照空格進(jìn)行分割
const arrOld = dataStr.split(' ')
// 4.2 循環(huán)分割后的數(shù)組,對每一項(xiàng)數(shù)據(jù),進(jìn)行字符串的替換操作
const arrNew = []
arrOld.forEach(item => {
arrNew.push(item.replace('=', ':'))
})
// 4.3 把新數(shù)組中的每一項(xiàng),進(jìn)行合并,得到一個(gè)新的字符串
const newStr = arrNew.join('\r\n')
// 5. 調(diào)用 fs.writeFile() 方法,把處理完畢的成績,寫入到新文件中
fs.writeFile('./day01/成績-ok.txt', newStr, function(err) {
if (err) {
return console.log('寫入文件失??!' + err.message)
}
console.log('成績寫入成功!')
})
})
split 方法
split() 方法接受一個(gè)參數(shù),該參數(shù)可以是一個(gè)字符串或正則表達(dá)式,用于指定分隔符。當(dāng) split() 方法被調(diào)用時(shí),字符串將根據(jù)指定的分隔符進(jìn)行拆分,并返回一個(gè)包含拆分后子串的數(shù)組。
const str = "apple banana cherry";
const arr = str.split(' '); // 使用空格作為分隔符
console.log(arr); // 輸出: ["apple", "banana", "cherry"]
string:要進(jìn)行分割的原始字符串。separator:指定的分隔符,可以是字符串或正則表達(dá)式。如果省略該參數(shù),則字符串會(huì)被當(dāng)作只包含一個(gè)元素的數(shù)組返回。limit:可選參數(shù),指定返回的數(shù)組的最大長度。如果設(shè)置了該參數(shù),數(shù)組將不會(huì)超過這個(gè)長度,多余的部分會(huì)被忽略。
push方法
向數(shù)組的末尾添加一個(gè)或多個(gè)元素,并返回新數(shù)組的長度
const arr = [1, 2, 3];
arr.push(4); // 添加單個(gè)元素
console.log(arr); // 輸出: [1, 2, 3, 4]
arr.push(5, 6); // 添加多個(gè)元素
console.log(arr); // 輸出: [1, 2, 3, 4, 5, 6]
array:要操作的數(shù)組。element1, element2, ..., elementN:要添加到數(shù)組末尾的一個(gè)或多個(gè)元素。
replace方法
字符串方法,用于替換字符串中的指定內(nèi)容。
const str = 'Hello, World!';
const newStr = str.replace('World', 'JavaScript');
console.log(newStr); // 輸出: Hello, JavaScript!
const str2 = 'apple, orange, banana, orange';
const newStr2 = str2.replace(/orange/g, 'grapefruit');
console.log(newStr2); // 輸出: apple, grapefruit, banana, grapefruit
str:要操作的字符串。regexp|substr:要被替換的內(nèi)容,可以是一個(gè)正則表達(dá)式或者一個(gè)字符串。newSubstr|function:替換后的新內(nèi)容,可以是一個(gè)字符串或者一個(gè)函數(shù)。如果第一個(gè)參數(shù)是一個(gè)字符串,replace 方法只會(huì)替換第一個(gè)匹配到的內(nèi)容。如果想要替換所有匹配到的內(nèi)容,可以使用正則表達(dá)式,并在正則表達(dá)式中加上 g 標(biāo)記。
join方法
將數(shù)組中的所有元素連接成一個(gè)字符串??梢灾付ㄒ粋€(gè)分隔符,在每個(gè)數(shù)組元素之間插入該分隔符,最終返回一個(gè)字符串。
const fruits = ['apple', 'banana', 'orange'];
const str = fruits.join(', ');
console.log(str); // 輸出: apple, banana, orange
const numbers = [1, 2, 3, 4, 5];
const str2 = numbers.join(' - ');
console.log(str2); // 輸出: 1 - 2 - 3 - 4 - 5
array:要操作的數(shù)組。separator:可選參數(shù),指定用于分隔數(shù)組元素的字符串。如果省略該參數(shù),則默認(rèn)使用逗號(hào)作為分隔符。
1.2文件路徑問題 path模塊
_ _dirname表示當(dāng)前文件所在的目錄
basename 提取文件名
const path = require('path')
// 定義文件的存放路徑
const fpath = '/a/b/c/index.html'
// const fullName = path.basename(fpath)
// console.log(fullName)
const nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt)
extname 提取文件的后綴名
const path = require('path')
// 這是文件的存放路徑
const fpath = '/a/b/c/index.html'
const fext = path.extname(fpath)
console.log(fext)
時(shí)鐘案例
提取文件中的內(nèi)容并寫入一個(gè)新的文件
// 1.1 導(dǎo)入 fs 模塊
const fs = require('fs')
// 1.2 導(dǎo)入 path 模塊
const path = require('path')
// 1.3 定義正則表達(dá)式,分別匹配 和 標(biāo)簽
const regStyle = /', '')
// 3.4 調(diào)用 fs.writeFile() 方法,將提取的樣式,寫入到 clock 目錄中 index.css 的文件里面
fs.writeFile(path.join(__dirname, './clock/index.css'), newCSS, function(err) {
if (err) return console.log('寫入 CSS 樣式失??!' + err.message)
console.log('寫入樣式文件成功!')
})
}
// 4.1 定義處理 js 腳本的方法
function resolveJS(htmlStr) {
// 4.2 通過正則,提取對應(yīng)的 標(biāo)簽內(nèi)容
const r2 = regScript.exec(htmlStr)
// 4.3 將提取出來的內(nèi)容,做進(jìn)一步的處理
const newJS = r2[0].replace('', '')
// 4.4 將處理的結(jié)果,寫入到 clock 目錄中的 index.js 文件里面
fs.writeFile(path.join(__dirname, './clock/index.js'), newJS, function(err) {
if (err) return console.log('寫入 JavaScript 腳本失??!' + err.message)
console.log('寫入 JS 腳本成功!')
})
}
// 5.1 定義處理 HTML 結(jié)構(gòu)的方法
function resolveHTML(htmlStr) {
// 5.2 將字符串調(diào)用 replace 方法,把內(nèi)嵌的 style 和 script 標(biāo)簽,替換為外聯(lián)的 link 和 script 標(biāo)簽
const newHTML = htmlStr.replace(regStyle, '').replace(regScript, '')
// 5.3 寫入 index.html 這個(gè)文件
fs.writeFile(path.join(__dirname, './clock/index.html'), newHTML, function(err) {
if (err) return console.log('寫入 HTML 文件失??!' + err.message)
console.log('寫入 HTML 頁面成功!')
})
}
fs.writeFile()方法只能用來創(chuàng)建文件不能用來創(chuàng)建路徑重復(fù)調(diào)用fs.writeFile()寫入同一個(gè)文件,新寫的內(nèi)容會(huì)覆蓋之前的內(nèi)容
1.3HTTP模塊
? http 模塊是 Node.js 官方提供的、用來創(chuàng)建 web 服務(wù)器的模塊。通過 http 模塊提供的 http.createServer0 方法,就能方便的把一臺(tái)普通的電腦,變成一臺(tái) Web 服務(wù)器,從而對外提供 Web 資源服務(wù),如果要希望使用 http 模塊創(chuàng)建 Web 服務(wù)器,則需要先導(dǎo)入它。
const http=require('http')
1.3.1IP 地址
IP 地址就是互聯(lián)網(wǎng)上每臺(tái)計(jì)算機(jī)的唯一地址,因此 !P地址具有唯一性。如果把“個(gè)人電腦”比作"一臺(tái)電話”,那么"P地址”就相當(dāng)于“電話號(hào)碼”,只有在知道對方IP 地址的前提下,才能與對應(yīng)的電腦之間進(jìn)行數(shù)據(jù)通信。IP地址的格式:通常用“點(diǎn)分十進(jìn)制”表示成(a.b.c.d)的形式,其中,a,b,cd 都是 0~255 之間的十進(jìn)制整數(shù)。例如:用點(diǎn)分十進(jìn)表示的IP地址(192.168.1.1) 注意: 互聯(lián)網(wǎng)中每臺(tái) Web 服務(wù)器,都有自己的IP 地址,例如:大家可以在 Windows 的終端中運(yùn)行 ping www.baidu.com 命令,即可查看到百度服務(wù)器的 IP 地址。 在開發(fā)期間,自己的電腦既是一臺(tái)服務(wù)器,也是一個(gè)客戶端,為了方便測試,可以在自己的瀏覽器中輸入 127.0.0.1 這個(gè) IP 地址,就能把自己的電腦當(dāng)做一臺(tái)服務(wù)器進(jìn)行訪問了。
1.3.2 域名和域名服務(wù)器
盡管 IP 地址能夠唯一地標(biāo)記網(wǎng)絡(luò)上的計(jì)算機(jī),但I(xiàn)P地址是一長串?dāng)?shù)字,不直觀,而且不便于記憶,于是人們又發(fā)明了另一套 字符型的地址方案,即所謂的域名(Domain Name)地址。 IP地址和域名是一一對應(yīng)的關(guān)系,這份對應(yīng)關(guān)系存放在一種叫做域名服務(wù)器(DNS,Domain name server)的電腦中。使用者 只需通過好記的域名訪問對應(yīng)的服務(wù)器即可,對應(yīng)的轉(zhuǎn)換工作由域名服務(wù)器實(shí)現(xiàn)。因此,域名服務(wù)器就是提供 IP 地址和域名 之間的轉(zhuǎn)換服務(wù)的服務(wù)器**。
注意:
① 單純使用 IP 地址,互聯(lián)網(wǎng)中的電腦也能夠正常工作。但是有了域名的加持,能讓互聯(lián)網(wǎng)的世界變得更加方便。② 在開發(fā)測試期間, 127.0.0.1 對應(yīng)的域名是 localhost,它們都代表我們自己的這臺(tái)電腦,在使用效果上沒有任何區(qū)別。
1.3.3 端口號(hào)
計(jì)算機(jī)中的端口號(hào),就好像是現(xiàn)實(shí)生活中的門牌號(hào)一樣。通過門牌號(hào),外賣小哥可以在整棟大樓眾多的房間中,準(zhǔn)確把外賣 送到你的手中。 同樣的道理,在一臺(tái)電腦中,可以運(yùn)行成百上千個(gè) web 服務(wù)。每個(gè) web 服務(wù)都對應(yīng)一個(gè)唯一的端口號(hào)。客戶端發(fā)送過來的 網(wǎng)絡(luò)請求,通過端口號(hào),可以被準(zhǔn)確地交給對應(yīng)的 web 服務(wù)進(jìn)行處理。
1.3.4創(chuàng)建基本的web服務(wù)器
// 1. 導(dǎo)入 http 模塊
const http = require('http')
// 2. 創(chuàng)建 web 服務(wù)器實(shí)例
const server = http.createServer()
// 3. 為服務(wù)器實(shí)例綁定 request 事件,監(jiān)聽客戶端的請求
server.on('request', function (req, res) {
console.log('Someone visit our web server.')
})
// 4. 啟動(dòng)服務(wù)器
server.listen(8090, function () {
console.log('server running at http://127.0.0.1:8090')
})
1.3.5請求返回對象
const http = require('http')
const server = http.createServer()
// req 是請求對象,包含了與客戶端相關(guān)的數(shù)據(jù)和屬性
server.on('request', (req, res) => {
// req.url 是客戶端請求的 URL 地址
const url = req.url
// req.method 是客戶端請求的 method 類型
const method = req.method
const str = `Your request url is ${url}, and request method is ${method}`
console.log(str)
// 調(diào)用 res.end() 方法,向客戶端響應(yīng)一些內(nèi)容
res.end(str)
})
server.listen(80, () => {
console.log('server running at http://127.0.0.1')
})
1.3.6解決中文亂碼問題
解決方法是這是響應(yīng)頭格式
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
// 定義一個(gè)字符串,包含中文的內(nèi)容
const str = `您請求的 URL 地址是 ${req.url},請求的 method 類型為 ${req.method}`
// 調(diào)用 res.setHeader() 方法,設(shè)置 Content-Type 響應(yīng)頭,解決中文亂碼的問題
res.setHeader('Content-Type', 'text/html; charset=utf-8')
// res.end() 將內(nèi)容響應(yīng)給客戶端
res.end(str)
})
server.listen(80, () => {
console.log('server running at http://127.0.0.1')
})
1.3.7服務(wù)器讀取本地資源
// 1.1 導(dǎo)入 http 模塊
const http = require('http')
// 1.2 導(dǎo)入 fs 模塊
const fs = require('fs')
// 1.3 導(dǎo)入 path 模塊
const path = require('path')
// 2.1 創(chuàng)建 web 服務(wù)器
const server = http.createServer()
// 2.2 監(jiān)聽 web 服務(wù)器的 request 事件
server.on('request', (req, res) => {
// 3.1 獲取到客戶端請求的 URL 地址
// /clock/index.html
// /clock/index.css
// /clock/index.js
const url = req.url
// 3.2 把請求的 URL 地址映射為具體文件的存放路徑
// const fpath = path.join(__dirname, url)
// 5.1 預(yù)定義一個(gè)空白的文件存放路徑
let fpath = ''
if (url === '/') {
fpath = path.join(__dirname, './clock/index.html')
} else {
// /index.html
// /index.css
// /index.js
fpath = path.join(__dirname, '/clock', url)
}
// 4.1 根據(jù)“映射”過來的文件路徑讀取文件的內(nèi)容
fs.readFile(fpath, 'utf8', (err, dataStr) => {
// 4.2 讀取失敗,向客戶端響應(yīng)固定的“錯(cuò)誤消息”
if (err) return res.end('404 Not found.')
// 4.3 讀取成功,將讀取成功的內(nèi)容,響應(yīng)給客戶端
res.end(dataStr)
})
})
// 2.3 啟動(dòng)服務(wù)器
server.listen(80, () => {
console.log('server running at http://127.0.0.1')
})
1.4模塊化
模塊化,就是遵守固定的規(guī)則,把一個(gè)大文件拆成獨(dú)立并互相依賴的多個(gè)小模塊。
① 提高了代碼的復(fù)用性② 提高了代碼的可維護(hù)性③ 可以實(shí)現(xiàn)按需加載
Node.js 中根據(jù)模塊來源的不同,將模塊分為了 3 大類,分別是:
內(nèi)置模塊(內(nèi)置模塊是由 Node.js 官方提供的,例如 fs、path、http 等)自定義模塊(用戶創(chuàng)建的每個(gè) .js 文件,都是自定義模塊)第三方模塊(由第三方開發(fā)出來的模塊,并非官方提供的內(nèi)置模塊,也不是用戶創(chuàng)建的自定義模塊,使用前需要先下載)
1.4.1加載模塊
const http = require('http')
const http = require('./custom.js')
//加載自定義模塊需要加上路徑 且使用require()方法加載其他模塊時(shí),會(huì)被執(zhí)行加載模塊中的代碼
// 且使用require加載自定義模塊的時(shí)候可以省略.js的后綴名
1.4.2模塊作用域
? 和函數(shù)作用域類似,在自定義模塊中定義的變量、方法等成員,只能在當(dāng)前模塊內(nèi)被訪問,這種模塊級(jí)別的訪問限制,叫做模塊作用域
防止了全局變量污染的問題
1.4.3module.exports 對象
? 在自定義模塊中,可以使用 module.exports 對象,將模塊內(nèi)的成員共享出去,供外界使用。
? 外界用 require() 方法導(dǎo)入自定義模塊時(shí),得到的就是 module.exports 所指向的對象。
// 在一個(gè)自定義模塊中,默認(rèn)情況下, module.exports = {}
const age = 20
// 向 module.exports 對象上掛載 username 屬性
module.exports.username = 'zs'
// 向 module.exports 對象上掛載 sayHello 方法
module.exports.sayHello = function() {
console.log('Hello!')
}
module.exports.age = age
// 讓 module.exports 指向一個(gè)全新的對象
module.exports = {
nickname: '小黑',
sayHi() {
console.log('Hi!')
}
}
且如果重復(fù)定義module.exports新的會(huì)覆蓋舊的 原理即地址值改變r(jià)equire() 模塊時(shí),得到的永遠(yuǎn)是 module.exports 指向的對象為了防止混亂,建議大家不要在同一個(gè)模塊中同時(shí)使用 exports 和 module.exports
1.4.4模塊規(guī)范
Node.js 遵循了 CommonJS 模塊化規(guī)范,CommonJS 規(guī)定了模塊的特性和各模塊之間如何相互依賴。
CommonJS 規(guī)定:
① 每個(gè)模塊內(nèi)部,module 變量代表當(dāng)前模塊。② module 變量是一個(gè)對象,它的 exports 屬性(即 module.exports)是對外的接口。③ 加載某個(gè)模塊,其實(shí)是加載該模塊的 module.exports 屬性。require() 方法用于加載模塊。
1.5npm和包
Node.js 中的第三方模塊又叫做包
不同于 Node.js 中的內(nèi)置模塊與自定義模塊,包是由第三方個(gè)人或團(tuán)隊(duì)開發(fā)出來的,免費(fèi)供所有人使用由于 Node.js 的內(nèi)置模塊僅提供了一些底層的 API,導(dǎo)致在基于內(nèi)置模塊進(jìn)行項(xiàng)目開發(fā)的時(shí),效率很低。包是基于內(nèi)置模塊封裝出來的,提供了更高級(jí)、更方便的 API,極大的提高了開發(fā)效率。包和內(nèi)置模塊之間的關(guān)系,類似于 jQuery 和 瀏覽器內(nèi)置 API 之間的關(guān)系
國外有一家 IT 公司,叫做 npm, Inc. 這家公司旗下有一個(gè)非常著名的網(wǎng)站: https://www.npmjs.com/ ,它是全球最 大的包共享平臺(tái),你可以從這個(gè)網(wǎng)站上搜索到任何你需要的包,只要你有足夠的耐心! 到目前位置,全球約 1100 多萬的開發(fā)人員,通過這個(gè)包共享平臺(tái),開發(fā)并共享了超過 120 多萬個(gè)包 供我們使用。 npm, Inc. 公司提供了一個(gè)地址為 https://registry.npmjs.org/ 的服務(wù)器,來對外共享所有的包,我們可以從這個(gè)服務(wù)器上下載自己所需要的包。
注意:
從 https://www.npmjs.com/ 網(wǎng)站上搜索自己所需要的包從 https://registry.npmjs.org/ 服務(wù)器上下載自己需要的包
npm, Inc. 公司提供了一個(gè)包管理工具,我們可以使用這個(gè)包管理工具,從 https://registry.npmjs.org/ 服務(wù)器把需要 的包下載到本地使用。 這個(gè)包管理工具的名字叫做 Node Package Manager(簡稱 npm 包管理工具),這個(gè)包管理工具隨著 Node.js 的安 裝包一起被安裝到了用戶的電腦上。 大家可以在終端中執(zhí)行 npm -v 命令,來查看自己電腦上所安裝的 npm 包管理工具的版本號(hào):
1.5.1格式化時(shí)間
格式化時(shí)間的高級(jí)做法
① 使用 npm 包管理工具,在項(xiàng)目中安裝格式化時(shí)間的包 moment② 使用 require() 導(dǎo)入格式化時(shí)間的包③ 參考 moment 的官方 API 文檔對時(shí)間進(jìn)行格式化npmjs.com官網(wǎng)去查看官方文檔
1.5.2下載外置第三方包
npm i moment node_modules 文件夾用來存放所有已安裝到項(xiàng)目中的包。require() 導(dǎo)入第三方包時(shí),就是從這個(gè)目錄中查找并加載包。 package-lock.json 配置文件用來記錄 node_modules 目錄下的每一個(gè)包的下載信息,例如包的名字、版本號(hào)、下載地址等。 注意:程序員不要手動(dòng)修改 node_modules 或 package-lock.json 文件中的任何代碼,npm 包管理工具會(huì)自動(dòng)維護(hù)它們。 默認(rèn)情況下,使用 npm install 命令安裝包的時(shí)候,會(huì)自動(dòng)安裝最新版本的包。如果需要安裝指定版本的包,可以在包名之后,通過 @ 符號(hào)指定具體的版本 包的版本號(hào)是以“點(diǎn)分十進(jìn)制”形式進(jìn)行定義的,總共有三位數(shù)字,例如 2.24.0 其中每一位數(shù)字所代表的的含義如下: 第1位數(shù)字:大版本 第2位數(shù)字:功能版本 第3位數(shù)字:Bug修復(fù)版本 版本號(hào)提升的規(guī)則:只要前面的版本號(hào)增長了,則后面的版本號(hào)歸零。
1.5.3 規(guī)范的包結(jié)構(gòu)
一個(gè)規(guī)范的包,它的組成結(jié)構(gòu),必須符合以下 3 點(diǎn)要求:包必須以單獨(dú)的目錄而存在包的頂級(jí)目錄下要必須包含 package.json這個(gè)包管理配置文件package.json 中必須包含 name,version,main 這三個(gè)屬性,分別代表包的名字、版本號(hào)、包的入口。注意:以上 3 點(diǎn)要求是一個(gè)規(guī)范的包結(jié)構(gòu)必須遵守的格式,關(guān)于更多的約束,可以參考如下網(wǎng)址:https://yarnpkg.com/zh-Hans/docs/package-json
1.5.4開發(fā)屬于自己的包
1.需要實(shí)現(xiàn)的功能
格式化日期轉(zhuǎn)義 HTML 中的特殊字符還原 HTML 中的特殊字符
2.將不同的功能進(jìn)行模塊拆分
將格式化時(shí)間的功能,拆分到 src -> dateFormat.js 中將處理 HTML 字符串的功能,拆分到 src -> htmlEscape.js 中在 index.js 中,導(dǎo)入兩個(gè)模塊,得到需要向外共享的方法在 index.js 中,使用 module.exports 把對應(yīng)的方法共享出去
3.編寫包的說明文檔
包根目錄中的 README.md 文件,是包的使用說明文檔。通過它,我們可以事先把包的使用說明,以 markdown 的格式寫出來,方便用戶參考。README 文件中具體寫什么內(nèi)容,沒有強(qiáng)制性的要求;只要能夠清晰地把包的作用、用法、注意事項(xiàng)等描述清楚即可。我們所創(chuàng)建的這個(gè)包的 README.md 文檔中,會(huì)包含以下 6 項(xiàng)內(nèi)容:安裝方式、導(dǎo)入方式、格式化時(shí)間、轉(zhuǎn)義 HTML 中的特殊字符、還原 HTML 中的特殊字符、開源協(xié)議
1.6npm賬號(hào)和發(fā)布
訪問 https://www.npmjs.com/ 網(wǎng)站,點(diǎn)擊 sign up 按鈕,進(jìn)入注冊用戶界面填寫賬號(hào)相關(guān)的信息:Full Name、Public Email、Username、Password點(diǎn)擊 Create an Account 按鈕,注冊賬號(hào)登錄郵箱,點(diǎn)擊驗(yàn)證鏈接,進(jìn)行賬號(hào)的驗(yàn)證
在終端npm login 隨后跳轉(zhuǎn)至官網(wǎng)登錄
1.6.1發(fā)布命令 npm publish
在項(xiàng)目的根目錄打開終端 npm publish 如果發(fā)布的時(shí)候出現(xiàn)403錯(cuò)誤,重命名項(xiàng)目包名并且下更改package.json中的包名
1.6.2刪除命令 npm unpublish 包名 --force命令 就可以刪除
npm unpublish命令只能刪除72小時(shí)內(nèi)的包npm unpublish 刪除的包,在 24 小時(shí)內(nèi)不允許重復(fù)發(fā)布發(fā)布包的時(shí)候要慎重,盡量不要往 npm 上發(fā)布沒有意義的包
1.7模塊的加載模式
1.7.1優(yōu)先從緩存中加載
模塊在第一次加載后會(huì)被緩存。 這也意味著多次調(diào)用 require() 不會(huì)導(dǎo)致模塊的代碼被執(zhí)行多次。注意:不論是內(nèi)置模塊、用戶自定義模塊、還是第三方模塊,它們都會(huì)優(yōu)先從緩存中加載,從而提高模塊的加載效率。
1.7.2內(nèi)置模塊的加載機(jī)制
內(nèi)置模塊是由 Node.js 官方提供的模塊,內(nèi)置模塊的加載優(yōu)先級(jí)最高。例如,require(‘fs’) 始終返回內(nèi)置的 fs 模塊,即使在 node_modules 目錄下有名字相同的包也叫做 fs。
1.7.3自定義模塊的加載機(jī)制
使用 require() 加載自定義模塊時(shí),必須指定以 ./ 或 …/ 開頭的路徑標(biāo)識(shí)符。在加載自定義模塊時(shí),如果沒有指定 ./ 或 …/ 這樣的路徑標(biāo)識(shí)符,則 node 會(huì)把它當(dāng)作內(nèi)置模塊或第三方模塊進(jìn)行加載。
同時(shí),在使用 require() 導(dǎo)入自定義模塊時(shí),如果省略了文件的擴(kuò)展名,則 Node.js 會(huì)按順序分別嘗試加載以下的文件:
按照確切的文件名進(jìn)行加載
補(bǔ)全 .js 擴(kuò)展名進(jìn)行加載補(bǔ)全 .json 擴(kuò)展名進(jìn)行加載補(bǔ)全 .node 擴(kuò)展名進(jìn)行加載加載失敗,終端報(bào)錯(cuò)
1.7.4第三方模塊的加載機(jī)制
如果傳遞給 require() 的模塊標(biāo)識(shí)符不是一個(gè)內(nèi)置模塊,也沒有以 ‘./’ 或 ‘…/’ 開頭,則 Node.js 會(huì)從當(dāng)前模塊的父目錄開始,嘗試從 /node_modules 文件夾中加載第三方模塊。
如果沒有找到對應(yīng)的第三方模塊,則移動(dòng)到再上一層父目錄中,進(jìn)行加載,直到文件系統(tǒng)的根目錄。
例如,假設(shè)在 ‘C:\Users\itheima\project\foo.js’ 文件里調(diào)用了 require(‘tools’),則 Node.js 會(huì)按以下順序查找:
C:\Users\itheima\project\node_modules\toolsC:\Users\itheima\node_modules\toolsC:\Users\node_modules\toolsC:\node_modules\tools
1.7.5目錄作為模塊
當(dāng)把目錄作為模塊標(biāo)識(shí)符,傳遞給 require() 進(jìn)行加載的時(shí)候,有三種加載方式:
在被加載的目錄下查找一個(gè)叫做 package.json 的文件,并尋找 main 屬性,作為 require() 加載的入口如果目錄里沒有 package.json 文件,或者 main 入口不存在或無法解析,則 Node.js 將會(huì)試圖加載目錄下的 index.js 文件。如果以上兩步都失敗了,則 Node.js 會(huì)在終端打印錯(cuò)誤消息,報(bào)告模塊的缺失:Error: Cannot find module ‘xxx’
2.express-路由-中間件
2.1express
Express:Express 是基于 Node.js 平臺(tái),快速、開放、極簡的 Web 開發(fā)框架。通俗的理解:Express 的作用和 Node.js 內(nèi)置的 http 模塊類似,是專門用來創(chuàng)建 Web 服務(wù)器的。Express 的本質(zhì):就是一個(gè) npm 上的第三方包,提供了快速創(chuàng)建 Web 服務(wù)器的便捷方法。Express 的中文官網(wǎng): http://www.expressjs.com.cn/
2.1.1Express能做什么
使用 Express,我們可以方便、快速的創(chuàng)建 Web 網(wǎng)站的服務(wù)器或 API 接口的服務(wù)器
Web 網(wǎng)站服務(wù)器:專門對外提供 Web 網(wǎng)頁資源的服務(wù)器。API 接口服務(wù)器:專門對外提供 API 接口的服務(wù)器。
2.2Express安裝和使用
在項(xiàng)目所處的目錄中,運(yùn)行如下的終端命令
npm i express@4.17.1
2.2.1基本使用
使用postman或者api測試工具可以測試接口
// 1. 導(dǎo)入 express
const express = require('express')
// 2. 創(chuàng)建 web 服務(wù)器
const app = express()
// 4. 監(jiān)聽客戶端的 GET 和 POST 請求,并向客戶端響應(yīng)具體的內(nèi)容
app.get('/user', (req, res) => {
// 調(diào)用 express 提供的 res.send() 方法,向客戶端響應(yīng)一個(gè) JSON 對象
res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
// 調(diào)用 express 提供的 res.send() 方法,向客戶端響應(yīng)一個(gè) 文本字符串
res.send('請求成功')
})
app.get('/', (req, res) => {
// 通過 req.query 可以獲取到客戶端發(fā)送過來的 查詢參數(shù)
// 注意:默認(rèn)情況下,req.query 是一個(gè)空對象
console.log(req.query)
res.send(req.query)
})
// 注意:這里的 :id 是一個(gè)動(dòng)態(tài)的參數(shù)
// http://127.0.0.1/user/3/ss
app.get('/user/:ids/:username', (req, res) => {
// req.params 是動(dòng)態(tài)匹配到的 URL 參數(shù),默認(rèn)也是一個(gè)空對象
console.log(req.params)
res.send(req.params)
})
// 3. 啟動(dòng) web 服務(wù)器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
2.2.2托管靜態(tài)資源
函數(shù),叫做 express.static(),通過它,我們可以非常方便地創(chuàng)建一個(gè)靜態(tài)資源服務(wù)器,例如,通過如下代碼就可以將 public 目錄下的圖片、CSS 文件、JavaScript 文件對外開放訪問了:注意:Express 在指定的靜態(tài)目錄中查找文件,并對外提供資源的訪問路徑。因此,存放靜態(tài)文件的目錄名不會(huì)出現(xiàn)在 URL 中。如果要托管多個(gè)靜態(tài)資源目錄,請多次調(diào)用 express.static() 函數(shù):訪問靜態(tài)資源文件時(shí),express.static() 函數(shù)會(huì)根據(jù)目錄的添加順序查找所需的文件如果希望在托管的靜態(tài)資源訪問路徑之前,掛載路徑前綴,則可以使用如下的方式:
const express = require('express')
const app = express()
// 在這里,調(diào)用 express.static() 方法,快速的對外提供靜態(tài)資源
app.use('/files', express.static('./files'))
app.use(express.static('./clock'))
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
2.3nodemon
注意:nodemon安裝的是全局環(huán)境 在主機(jī)安裝不是vscode在編寫調(diào)試 Node.js 項(xiàng)目的時(shí)候,如果修改了項(xiàng)目的代碼,則需要頻繁的手動(dòng) close 掉,然后再重新啟動(dòng),非常繁瑣。現(xiàn)在,我們可以使用 nodemon(https://www.npmjs.com/package/nodemon) 這個(gè)工具,它能夠監(jiān)聽項(xiàng)目文件的變動(dòng),當(dāng)代碼被修改后,nodemon 會(huì)自動(dòng)幫我們重啟項(xiàng)目,極大方便了開發(fā)和調(diào)試。啟動(dòng)項(xiàng)目時(shí)把node換成nodemon
2.4路由
映射關(guān)系
const express = require('express')
const app = express()
// 掛載路由
app.get('/', (req, res) => {
res.send('hello world.')
})
app.post('/', (req, res) => {
res.send('Post Request.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
2.4.1模塊化路由
為了方便對路由進(jìn)行模塊化的管理,Express 不建議將路由直接掛載到 app 上,而是推薦將路由抽離為單獨(dú)的模塊。
將路由抽離為單獨(dú)模塊的步驟如下:
創(chuàng)建路由模塊對應(yīng)的 .js 文件調(diào)用 express.Router() 函數(shù)創(chuàng)建路由對象向路由對象上掛載具體的路由使用 module.exports 向外共享路由對象使用 app.use() 函數(shù)注冊路由模塊
類似于托管靜態(tài)資源時(shí),為靜態(tài)資源統(tǒng)一掛載訪問前綴一樣,路由模塊添加前綴的方式也非常簡單:
router.js
// 這是路由模塊
// 1. 導(dǎo)入 express
const express = require('express')
// 2. 創(chuàng)建路由對象
const router = express.Router()
// 3. 掛載具體的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外導(dǎo)出路由對象
module.exports = router
模塊兒路由.js
const express = require('express')
const app = express()
// app.use('/files', express.static('./files'))
// 1. 導(dǎo)入路由模塊
const router = require('./03.router')
// 2. 注冊路由模塊
app.use('/api', router)
// 注意: app.use() 函數(shù)的作用,就是來注冊全局中間件
app.listen(80, () => {
console.log('http://127.0.0.1')
})
測試路徑
http://127.0.0.1/api/user/list
2.5中間件
? 中間件(Middleware ),特指業(yè)務(wù)流程的中間處理環(huán)節(jié)。
2.5.1概念和格式
調(diào)用流程
當(dāng)一個(gè)請求到達(dá) Express 的服務(wù)器之后,可以連續(xù)調(diào)用多個(gè)中間件,從而對這次請求進(jìn)行預(yù)處理。
格式
Express 的中間件,本質(zhì)上就是一個(gè) function 處理函數(shù),Express 中間件的格式如下:中間件函數(shù)的形參列表中,必須包含 next 參數(shù)。而路由處理函數(shù)中只包含 req 和 res。next 函數(shù)是實(shí)現(xiàn)多個(gè)中間件連續(xù)調(diào)用的關(guān)鍵,它表示把流轉(zhuǎn)關(guān)系轉(zhuǎn)交給下一個(gè)中間件或路由。
2.5.2全局生效的中間件
const express = require('express')
const app = express()
// // 定義一個(gè)最簡單的中間件函數(shù)
// const mw = function (req, res, next) {
// console.log('這是最簡單的中間件函數(shù)')
// // 把流轉(zhuǎn)關(guān)系,轉(zhuǎn)交給下一個(gè)中間件或路由
// next()
// }
// // 將 mw 注冊為全局生效的中間件
// app.use(mw)
// 這是定義全局中間件的簡化形式
app.use((req, res, next) => {
console.log('這是最簡單的中間件函數(shù)')
next()
})
2.5.3中間件的作用
多個(gè)中間件之間,共享同一份 req 和 res?;谶@樣的特性,我們可以在上游的中間件中,統(tǒng)一為 req 或 res 對象添加自定義的屬性或方法,供下游的中間件或路由進(jìn)行使用。
const express = require('express')
const app = express()
// 這是定義全局中間件的簡化形式
app.use((req, res, next) => {
// 獲取到請求到達(dá)服務(wù)器的時(shí)間
const time = Date.now()
// 為 req 對象,掛載自定義屬性,從而把時(shí)間共享給后面的所有路由
req.startTime = time
next()
})
app.get('/', (req, res) => {
res.send('Home page.' + req.startTime)
})
app.get('/user', (req, res) => {
res.send('User page.' + req.startTime)
})
2.5.4定義多個(gè)中間件
可以使用 app.use() 連續(xù)定義多個(gè)全局中間件??蛻舳苏埱蟮竭_(dá)服務(wù)器之后,會(huì)按照中間件定義的先后順序依次進(jìn)行調(diào)用,示例代碼如下:
const express = require('express')
const app = express()
// 定義第一個(gè)全局中間件
app.use((req, res, next) => {
console.log('調(diào)用了第1個(gè)全局中間件')
next()
})
// 定義第二個(gè)全局中間件
app.use((req, res, next) => {
console.log('調(diào)用了第2個(gè)全局中間件')
next()
})
// 定義一個(gè)路由
app.get('/user', (req, res) => {
res.send('User page.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
2.5.5局部生效的中間件
不使用 app.use() 定義的中間件,叫做局部生效的中間件,示例代碼如下:
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// 1. 定義中間件函數(shù)
const mw1 = (req, res, next) => {
console.log('調(diào)用了局部生效的中間件')
next()
}
// 2. 創(chuàng)建路由
app.get('/', mw1, (req, res) => {
res.send('Home page.')
})
app.get('/user', (req, res) => {
res.send('User page.')
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
2.5.6同時(shí)使用多個(gè)中間件
app.get('/', [mw1, mw2], (req, res) => {
res.send('Home page.')
})
app.get('/', mw1, mw2, (req, res) => {
res.send('Home page.')
})
//二者等價(jià)
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// 1. 定義中間件函數(shù)
const mw1 = (req, res, next) => {
console.log('調(diào)用了第一個(gè)局部生效的中間件')
next()
}
const mw2 = (req, res, next) => {
console.log('調(diào)用了第二個(gè)局部生效的中間件')
next()
}
// 2. 創(chuàng)建路由
app.get('/', [mw1, mw2], (req, res) => {
res.send('Home page.')
})
app.get('/user', (req, res) => {
res.send('User page.')
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
2.5.7注意事項(xiàng)
一定要在路由之前注冊中間件客戶端發(fā)送過來的請求,可以連續(xù)調(diào)用多個(gè)中間件進(jìn)行處理執(zhí)行完中間件的業(yè)務(wù)代碼之后,不要忘記調(diào)用 next() 函數(shù)為了防止代碼邏輯混亂,調(diào)用 next() 函數(shù)后不要再寫額外的代碼連續(xù)調(diào)用多個(gè)中間件時(shí),多個(gè)中間件之間,共享 req 和 res 對象
2.5.8中間件的分類——五大類
應(yīng)用級(jí)別的中間件路由級(jí)別的中間件錯(cuò)誤級(jí)別的中間件Express 內(nèi)置的中間件第三方的中間件
2.5.8.1 應(yīng)用級(jí)別的中間件
通過app.use()或app.get()或者app.post(),綁定到app上的中間件叫做應(yīng)用級(jí)別的中間件
app.use((req, res, next) => {
console.log('這是最簡單的中間件函數(shù)')
next()
})
app.get('/', (req, res) => {
console.log('調(diào)用了 / 這個(gè)路由')
res.send('Home page.')
})
app.get('/user', (req, res) => {
console.log('調(diào)用了 /user 這個(gè)路由')
res.send('User page.')
})
2.5.8.2路由級(jí)別的中間件
綁定到 express.Router() 實(shí)例上的中間件,叫做路由級(jí)別的中間件。它的用法和應(yīng)用級(jí)別中間件沒有任何區(qū)別。只不過,應(yīng)用級(jí)別中間件是綁定到 app 實(shí)例上,路由級(jí)別中間件綁定到 router 實(shí)例上,代碼示例如下:
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
2.5.8.3錯(cuò)誤級(jí)別到中間件
錯(cuò)誤級(jí)別中間件的作用:專門用來捕獲整個(gè)項(xiàng)目中發(fā)生的異常錯(cuò)誤,從而防止項(xiàng)目異常崩潰的問題。格式:錯(cuò)誤級(jí)別中間件的 function 處理函數(shù)中,必須有 4 個(gè)形參,形參順序從前到后,分別是 (err, req, res, next)。注意:錯(cuò)誤級(jí)別的中間件,必須注冊在所有路由之后!
// 2. 定義錯(cuò)誤級(jí)別的中間件,捕獲整個(gè)項(xiàng)目的異常錯(cuò)誤,從而防止程序的崩潰
app.use((err, req, res, next) => {
console.log('發(fā)生了錯(cuò)誤!' + err.message)
res.send('Error:' + err.message)
})
2.5.8.4 內(nèi)置的中間件
自4.16.0版本開始 內(nèi)置了三個(gè)常用的中間件
express.static 快速托管靜態(tài)資源的內(nèi)置中間件,例如: HTML 文件、圖片、CSS 樣式等(無兼容性)express.json 解析 JSON 格式的請求體數(shù)據(jù)(有兼容性,僅在 4.16.0+ 版本中可用)express.urlencoded 解析 URL-encoded 格式的請求體數(shù)據(jù)(有兼容性,僅在 4.16.0+ 版本中可用)
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// 注意:除了錯(cuò)誤級(jí)別的中間件,其他的中間件,必須在路由之前進(jìn)行配置
// 通過 express.json() 這個(gè)中間件,解析表單中的 JSON 格式的數(shù)據(jù)
app.use(express.json())
// 通過 express.urlencoded() 這個(gè)中間件,來解析 表單中的 url-encoded 格式的數(shù)據(jù)
app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
// 在服務(wù)器,可以使用 req.body 這個(gè)屬性,來接收客戶端發(fā)送過來的請求體數(shù)據(jù)
// 默認(rèn)情況下,如果不配置解析表單數(shù)據(jù)的中間件,則 req.body 默認(rèn)等于 undefined
console.log(req.body)
res.send('ok')
})
app.post('/book', (req, res) => {
// 在服務(wù)器端,可以通過 req,body 來獲取 JSON 格式的表單數(shù)據(jù)和 url-encoded 格式的數(shù)據(jù)
console.log(req.body)
res.send('ok')
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
2.5.8.5第三方中間件
非 Express 官方內(nèi)置的,而是由第三方開發(fā)出來的中間件,叫做第三方中間件。在項(xiàng)目中,大家可以按需下載并配置第三方中間件,從而提高項(xiàng)目的開發(fā)效率。
例如:在 express@4.16.0 之前的版本中,經(jīng)常使用 body-parser 這個(gè)第三方中間件,來解析請求體數(shù)據(jù)。
使用步驟如下:
運(yùn)行 npm install body-parser 安裝中間件使用 require 導(dǎo)入中間件調(diào)用 app.use() 注冊并使用中間件注意:Express 內(nèi)置的 express.urlencoded 中間件,就是基于 body-parser 這個(gè)第三方中間件進(jìn)一步封裝出來的。
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// 1. 導(dǎo)入解析表單數(shù)據(jù)的中間件 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 注冊中間件
app.use(parser.urlencoded({ extended: false }))
// app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
// 如果沒有配置任何解析表單數(shù)據(jù)的中間件,則 req.body 默認(rèn)等于 undefined
console.log(req.body)
res.send('ok')
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
2.5.9自定義中間件
需求描述與實(shí)現(xiàn)步驟
自己手動(dòng)模擬一個(gè)類似于 express.urlencoded 這樣的中間件,來解析 POST 提交到服務(wù)器的表單數(shù)據(jù)。
實(shí)現(xiàn)步驟:
定義中間件 // 這是解析表單數(shù)據(jù)的中間件
app.use((req, res, next) => {
// 定義中間件具體的業(yè)務(wù)邏輯
})
監(jiān)聽 req 的 data 事件 在中間件中,需要監(jiān)聽 req 對象的 data 事件,來獲取客戶端發(fā)送到服務(wù)器的數(shù)據(jù)。如果數(shù)據(jù)量比較大,無法一次性發(fā)送完畢,則客戶端會(huì)把數(shù)據(jù)切割后,分批發(fā)送到服務(wù)器。所以 data 事件可能會(huì)觸發(fā)多次,每一次觸發(fā) data 事件時(shí),獲取到數(shù)據(jù)只是完整數(shù)據(jù)的一部分,需要手動(dòng)對接收到的數(shù)據(jù)進(jìn)行拼接。 // 1. 定義一個(gè) str 字符串,專門用來存儲(chǔ)客戶端發(fā)送過來的請求體數(shù)據(jù)
let str = ''
// 2. 監(jiān)聽 req 的 data 事件
req.on('data', (chunk) => {
str += chunk
監(jiān)聽 req 的 end 事件 當(dāng)請求體數(shù)據(jù)接收完畢之后,會(huì)自動(dòng)觸發(fā) req 的 end 事件。因此,我們可以在 req 的 end 事件中,拿到并處理完整的請求體數(shù)據(jù)。示例代碼如下: req.on('end', () => {
console.log(str)
使用 querystring 模塊解析請求體數(shù)據(jù) Node.js 內(nèi)置了一個(gè) querystring 模塊,專門用來處理查詢字符串。通過這個(gè)模塊提供的 parse() 函數(shù),可以輕松把查詢字符串,解析成對象的格式。示例代碼如下 // 導(dǎo)入 Node.js 內(nèi)置的 querystring 模塊
const qs = require('querystring')
將解析出來的數(shù)據(jù)對象掛載為 req.body 上游的中間件和下游的中間件及路由之間,共享同一份 req 和 res。因此,我們可以將解析出來的數(shù)據(jù),掛載為 req 的自定義屬性,命名為 req.body,供下游使用。示例代碼如下: req.on('end', () => {
// 在 str 中存放的是完整的請求體數(shù)據(jù)
// console.log(str)
// TODO: 把字符串格式的請求體數(shù)據(jù),解析成對象格式
const body = qs.parse(str)
req.body = body
next()
})
將自定義中間件封裝為模塊 為了優(yōu)化代碼的結(jié)構(gòu),我們可以把自定義的中間件函數(shù),封裝為獨(dú)立的模塊,示例代碼如下
// 導(dǎo)入 Node.js 內(nèi)置的 querystring 模塊
const qs = require('querystring')
const bodyParser = (req, res, next) => {
// 定義中間件具體的業(yè)務(wù)邏輯
// 1. 定義一個(gè) str 字符串,專門用來存儲(chǔ)客戶端發(fā)送過來的請求體數(shù)據(jù)
let str = ''
// 2. 監(jiān)聽 req 的 data 事件
req.on('data', (chunk) => {
str += chunk
})
// 3. 監(jiān)聽 req 的 end 事件
req.on('end', () => {
// 在 str 中存放的是完整的請求體數(shù)據(jù)
// console.log(str)
// TODO: 把字符串格式的請求體數(shù)據(jù),解析成對象格式
const body = qs.parse(str)
req.body = body
next()
})
}
module.exports = bodyParser
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// 1. 導(dǎo)入自己封裝的中間件模塊
const customBodyParser = require('./14.custom-body-parser')
// 2. 將自定義的中間件函數(shù),注冊為全局可用的中間件
app.use(customBodyParser)
app.post('/user', (req, res) => {
res.send(req.body)
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
3.使用express寫接口
3.1基本接口
1.創(chuàng)建基本的服務(wù)器
2.創(chuàng)建 API 路由模塊 apiroter.js
3.編寫GET POST接口
注意:如果要獲取 URL-encoded 格式的請求體數(shù)據(jù),必須配置中間件 app.use(express.urlencoded({ extended: false }))
// 導(dǎo)入 express
const express = require('express')
// 創(chuàng)建服務(wù)器實(shí)例
const app = express()
// 配置解析表單數(shù)據(jù)的中間件
app.use(express.urlencoded({ extended: false }))
// 必須在配置 cors 中間件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// TODO: 定義 JSONP 接口具體的實(shí)現(xiàn)過程
// 1. 得到函數(shù)的名稱
const funcName = req.query.callback
// 2. 定義要發(fā)送到客戶端的數(shù)據(jù)對象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一個(gè)函數(shù)的調(diào)用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,響應(yīng)給客戶端
res.send(scriptStr)
})
// 一定要在路由之前,配置 cors 這個(gè)中間件,從而解決接口跨域的問題
const cors = require('cors')
app.use(cors())
// 導(dǎo)入路由模塊
const router = require('./16.apiRouter')
// 把路由模塊,注冊到 app 上
app.use('/api', router)
// 啟動(dòng)服務(wù)器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
apiroter.js
const express = require('express')
const router = express.Router()
// 在這里掛載對應(yīng)的路由
router.get('/get', (req, res) => {
// 通過 req.query 獲取客戶端通過查詢字符串,發(fā)送到服務(wù)器的數(shù)據(jù)
const query = req.query
// 調(diào)用 res.send() 方法,向客戶端響應(yīng)處理的結(jié)果
res.send({
status: 0, // 0 表示處理成功,1 表示處理失敗
msg: 'GET 請求成功!', // 狀態(tài)的描述
data: query, // 需要響應(yīng)給客戶端的數(shù)據(jù)
})
})
// 定義 POST 接口
router.post('/post', (req, res) => {
// 通過 req.body 獲取請求體中包含的 url-encoded 格式的數(shù)據(jù)
const body = req.body
// 調(diào)用 res.send() 方法,向客戶端響應(yīng)結(jié)果
res.send({
status: 0,
msg: 'POST 請求成功!',
data: body,
})
})
// 定義 DELETE 接口
router.delete('/delete', (req, res) => {
res.send({
status: 0,
msg: 'DELETE請求成功',
})
})
module.exports = router
3.2跨域問題
剛才編寫的 GET 和 POST接口,存在一個(gè)很嚴(yán)重的問題:不支持跨域請求。
解決接口跨域問題的方案主要有兩種:
CORS(主流的解決方案,推薦使用)JSONP(有缺陷的解決方案:只支持 GET 請求)
3.2.1CORS中間件
cors 是 Express 的一個(gè)第三方中間件。通過安裝和配置 cors 中間件,可以很方便地解決跨域問題。
使用步驟分為如下 3 步:
運(yùn)行 npm install cors 安裝中間件使用 const cors = require(‘cors’) 導(dǎo)入中間件在路由之前調(diào)用 app.use(cors()) 配置中間件
CORS (Cross-Origin Resource Sharing,跨域資源共享)由一系列 HTTP 響應(yīng)頭組成,這些 HTTP 響應(yīng)頭決定瀏覽器是否阻止前端 JS 代碼跨域獲取資源。瀏覽器的同源安全策略默認(rèn)會(huì)阻止網(wǎng)頁“跨域”獲取資源。但如果接口服務(wù)器配置了 CORS 相關(guān)的 HTTP 響應(yīng)頭,就可以解除瀏覽器端的跨域訪問限制。
注意事項(xiàng):
CORS 主要在服務(wù)器端進(jìn)行配置??蛻舳藶g覽器無須做任何額外的配置,即可請求開啟了 CORS 的接口。CORS 在瀏覽器中有兼容性。只有支持 XMLHttpRequest Level2 的瀏覽器,才能正常訪問開啟了 CORS 的服務(wù)端接口(例如:IE10+、Chrome4+、FireFox3.5+)。
3.3跨域資源共享
CORS 響應(yīng)頭部 - Access-Control-Allow-Origin
響應(yīng)頭部中可以攜帶一個(gè) Access-Control-Allow-Origin 字段,其語法如下:
Access-Control-Allow-Origin:
其中,origin 參數(shù)的值指定了允許訪問該資源的外域 URL。例如,下面的字段值將只允許來自 http://itcast.cn 的請求:
res.setHeader('Access-Control-Allow-Origin','http://itca.cn'
3.4JSONP接口
概念:瀏覽器端通過
特點(diǎn):
JSONP 不屬于真正的 Ajax 請求,因?yàn)樗鼪]有使用 XMLHttpRequest 這個(gè)對象。JSONP 僅支持 GET 請求,不支持 POST、PUT、DELETE 等請求。
如果項(xiàng)目中已經(jīng)配置了 CORS 跨域資源共享,為了防止沖突,必須在配置 CORS 中間件之前聲明 JSONP 的接口。否則 JSONP 接口會(huì)被處理成開啟了 CORS 的接口。示例代碼如下:
app.get('/api/jsonp', (req, res) => {
})
// 一定要在路由之前,配置 cors 這個(gè)中間件,從而解決接口跨域的問題
const cors = require('cors')
app.use(cors())
實(shí)現(xiàn)步驟:
獲取客戶端發(fā)送過來的回調(diào)函數(shù)的名字得到要通過 JSONP 形式發(fā)送給客戶端的數(shù)據(jù)根據(jù)前兩步得到的數(shù)據(jù),拼接出一個(gè)函數(shù)調(diào)用的字符串把上一步拼接得到的字符串,響應(yīng)給客戶端的
app.get('/api/jsonp', (req, res) => {
// TODO: 定義 JSONP 接口具體的實(shí)現(xiàn)過程
// 1. 得到函數(shù)的名稱
const funcName = req.query.callback
// 2. 定義要發(fā)送到客戶端的數(shù)據(jù)對象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一個(gè)函數(shù)的調(diào)用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,響應(yīng)給客戶端
res.send(scriptStr)
})
在網(wǎng)頁中使用 jQuery 發(fā)起 JSONP 請求
調(diào)用 $.ajax() 函數(shù),提供 JSONP 的配置選項(xiàng),從而發(fā)起 JSONP 請求,示例代碼如下:
// 4. 為 JSONP 按鈕綁定點(diǎn)擊事件處理函數(shù)
$('#btnJSONP').on('click', function () {
$.ajax({
type: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success: function (res) {
console.log(res)
},
})
})
4.mysql部分
略
4.1排序
ORDER BY 語句用于根據(jù)指定的列對結(jié)果集進(jìn)行排序。ORDER BY 語句默認(rèn)按照升序?qū)τ涗涍M(jìn)行排序。如果您希望按照降序?qū)τ涗涍M(jìn)行排序,可以使用 DESC 關(guān)鍵字。
升序
select * from users order by status;select * from users order by status ASC; 二者等同且都是升序排序
降序
select * from users order by id DESC; 指定DESC
多重排序
對 users 表中的數(shù)據(jù),先按照 status 字段進(jìn)行降序排序,再按照 username 的字母順序,進(jìn)行升序排序,示例如下:select * from users order by status DESC,username ASC
4.2函數(shù)和語句
統(tǒng)計(jì)函數(shù):
COUNT(*) 函數(shù)用于返回查詢結(jié)果的總數(shù)據(jù)條數(shù),語法格式如下:select count(*) from user where status=0
使用AS為列設(shè)置別名
如果希望給查詢出來的列名稱設(shè)置別名,可以使用 AS 關(guān)鍵字,示例如下:select count (*) as total from users where status=0
5.Mysql模塊
5.1在數(shù)據(jù)庫中操作數(shù)據(jù)庫的步驟
安裝操作 MySQL 數(shù)據(jù)庫的第三方模塊(mysql)通過 mysql 模塊連接到 MySQL 數(shù)據(jù)庫通過 mysql 模塊執(zhí)行 SQL 語句
安裝 mysql 模塊
mysql 模塊是托管于 npm 上的第三方模塊。它提供了在 Node.js 項(xiàng)目中連接和操作 MySQL 數(shù)據(jù)庫的能力。想要在項(xiàng)目中使用它,需要先運(yùn)行如下命令,將 mysql 安裝為項(xiàng)目的依賴包:npm install mysql使用 mysql 模塊操作 MySQL 數(shù)據(jù)庫之前,必須先對 mysql 模塊進(jìn)行必要的配置,主要的配置步驟如下:
// 1. 導(dǎo)入 mysql 模塊
const mysql = require('mysql')
// 2. 建立與 MySQL 數(shù)據(jù)庫的連接關(guān)系
const db = mysql.createPool({
host: '127.0.0.1', // 數(shù)據(jù)庫的 IP 地址 8080
user: 'root', // 登錄數(shù)據(jù)庫的賬號(hào)
password: '248690', // 登錄數(shù)據(jù)庫的密碼
database: 'mybatis', // 指定要操作哪個(gè)數(shù)據(jù)庫
})
測試:
// 測試 mysql 模塊能否正常工作
db.query('select 1', (err, results) => {
// mysql 模塊工作期間報(bào)錯(cuò)了
if(err) return console.log(err.message)
// 能夠成功的執(zhí)行 SQL 語句
console.log(results)
})
正?;貍?/p>
[ RowDataPacket { '1': 1 } ]
查詢
const sqlStr = 'select * from users'
db.query(sqlStr, (err, results) => {
// 查詢數(shù)據(jù)失敗
if (err) return console.log(err.message)
// 查詢數(shù)據(jù)成功
// 注意:如果執(zhí)行的是 select 查詢語句,則執(zhí)行的結(jié)果是數(shù)組
console.log(results)
})
插入
const user = { username: 'Spider-Man', password: 'pcc123' }
// 定義待執(zhí)行的 SQL 語句
const sqlStr = 'insert into users (username, password) values (?, ?)'
// 執(zhí)行 SQL 語句
db.query(sqlStr, [user.username, user.password], (err, results) => {
// 執(zhí)行 SQL 語句失敗了
if (err) return console.log(err.message)
// 成功了
// 注意:如果執(zhí)行的是 insert into 插入語句,則 results 是一個(gè)對象
// 可以通過 affectedRows 屬性,來判斷是否插入數(shù)據(jù)成功
if (results.affectedRows === 1) {
console.log('插入數(shù)據(jù)成功!')
}
})
插入數(shù)據(jù)的便捷方法
const user = { username: 'Spider-Man2', password: 'pcc4321' }
// 定義待執(zhí)行的 SQL 語句
const sqlStr = 'insert into users set ?'
// 執(zhí)行 SQL 語句
db.query(sqlStr, user, (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('插入數(shù)據(jù)成功')
}
})
更新
const user = { id: 6, username: 'aaa', password: '000' }
// 定義 SQL 語句
const sqlStr = 'update users set username=?, password=? where id=?'
// 執(zhí)行 SQL 語句
db.query(sqlStr, [user.username, user.password, user.id], (err, results) => {
if (err) return console.log(err.message)
// 注意:執(zhí)行了 update 語句之后,執(zhí)行的結(jié)果,也是一個(gè)對象,可以通過 affectedRows 判斷是否更新成功
if (results.affectedRows === 1) {
console.log('更新成功')
}
})
便捷
const user = { id: 6, username: 'aaaa', password: '0000' }
// 定義 SQL 語句
const sqlStr = 'update users set ? where id=?'
// 執(zhí)行 SQL 語句
db.query(sqlStr, [user, user.id], (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新數(shù)據(jù)成功')
}
})
刪除
const sqlStr = 'delete from users where id=?'
db.query(sqlStr, 5, (err, results) => {
if (err) return console.log(err.message)
// 注意:執(zhí)行 delete 語句之后,結(jié)果也是一個(gè)對象,也會(huì)包含 affectedRows 屬性
if (results.affectedRows === 1) {
console.log('刪除數(shù)據(jù)成功')
}
})
標(biāo)記刪除
const sqlStr = 'update users set status=? where id=?'
db.query(sqlStr, [1, 6], (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('標(biāo)記刪除成功')
}
})
6.WEB開發(fā)模塊1
目前主流的 Web 開發(fā)模式有兩種,分別是:
基于服務(wù)端渲染的傳統(tǒng) Web 開發(fā)模式
服務(wù)端渲染的概念:服務(wù)器發(fā)送給客戶端的 HTML 頁面,是在服務(wù)器通過字符串的拼接,動(dòng)態(tài)生成的。因此,客戶端不需要使用 Ajax 這樣的技術(shù)額外請求頁面的數(shù)據(jù)。代碼示例如下:
優(yōu)點(diǎn):
前端耗時(shí)少。因?yàn)榉?wù)器端負(fù)責(zé)動(dòng)態(tài)生成 HTML 內(nèi)容,瀏覽器只需要直接渲染頁面即可。尤其是移動(dòng)端,更省電。有利于SEO。因?yàn)榉?wù)器端響應(yīng)的是完整的 HTML 頁面內(nèi)容,所以爬蟲更容易爬取獲得信息,更有利于 SEO。
缺點(diǎn):
占用服務(wù)器端資源。即服務(wù)器端完成 HTML 頁面內(nèi)容的拼接,如果請求較多,會(huì)對服務(wù)器造成一定的訪問壓力。不利于前后端分離,開發(fā)效率低。使用服務(wù)器端渲染,則無法進(jìn)行分工合作,尤其對于前端復(fù)雜度高的項(xiàng)目,不利于項(xiàng)目高效開
基于前后端分離的新型 Web 開發(fā)模式
前后端分離的概念:前后端分離的開發(fā)模式,依賴于 Ajax 技術(shù)的廣泛應(yīng)用。簡而言之,前后端分離的 Web 開發(fā)模式,就是后端只負(fù)責(zé)提供 API 接口,前端使用 Ajax 調(diào)用接口的開發(fā)模式。
優(yōu)點(diǎn):
開發(fā)體驗(yàn)好。前端專注于 UI 頁面的開發(fā),后端專注于api 的開發(fā),且前端有更多的選擇性。用戶體驗(yàn)好。Ajax 技術(shù)的廣泛應(yīng)用,極大的提高了用戶的體驗(yàn),可以輕松實(shí)現(xiàn)頁面的局部刷新。減輕了服務(wù)器端的渲染壓力。因?yàn)轫撁孀罱K是在每個(gè)用戶的瀏覽器中生成的。
缺點(diǎn):
不利于 SEO。因?yàn)橥暾?HTML 頁面需要在客戶端動(dòng)態(tài)拼接完成,所以爬蟲對無法爬取頁面的有效信息。(解決方案:利用 Vue、React 等前端框架的 SSR (server side render)技術(shù)能夠很好的解決 SEO 問題?。?/p>
比如企業(yè)級(jí)網(wǎng)站,主要功能是展示而沒有復(fù)雜的交互,并且需要良好的 SEO,則這時(shí)我們就需要使用服務(wù)器端渲染;而類似后臺(tái)管理項(xiàng)目,交互性比較強(qiáng),不需要考慮 SEO,那么就可以使用前后端分離的開發(fā)模式。另外,具體使用何種開發(fā)模式并不是絕對的,為了同時(shí)兼顧了首頁的渲染速度和前后端分離的開發(fā)效率,一些網(wǎng)站采用了首屏服務(wù)器端渲染 + 其他頁面前后端分離的開發(fā)模式。
6.1身份認(rèn)證
身份認(rèn)證(Authentication)又稱“身份驗(yàn)證”、“鑒權(quán)”,是指通過一定的手段,完成對用戶身份的確認(rèn)。日常生活中的身份認(rèn)證隨處可見,例如:高鐵的驗(yàn)票乘車,手機(jī)的密碼或指紋解鎖,支付寶或微信的支付密碼等。在 Web 開發(fā)中,也涉及到用戶身份的認(rèn)證,例如:各大網(wǎng)站的手機(jī)驗(yàn)證碼登錄、郵箱密碼登錄、二維碼登錄等。
6.1.1不同開發(fā)模式下的身份認(rèn)證
對于服務(wù)端渲染和前后端分離這兩種開發(fā)模式來說,分別有著不同的身份認(rèn)證方案:
服務(wù)端渲染推薦使用 Session 認(rèn)證機(jī)制前后端分離推薦使用 JWT 認(rèn)證機(jī)制
6.2Session認(rèn)證
6.2.1 HTTP 協(xié)議的無狀態(tài)性
了解 HTTP 協(xié)議的無狀態(tài)性是進(jìn)一步學(xué)習(xí) Session 認(rèn)證機(jī)制的必要前提。HTTP 協(xié)議的無狀態(tài)性,指的是客戶端的每次 HTTP 請求都是獨(dú)立的,連續(xù)多個(gè)請求之間沒有直接的關(guān)系,服務(wù)器不會(huì)主動(dòng)保留每次 HTTP 請求的狀態(tài)。注意:現(xiàn)實(shí)生活中的會(huì)員卡身份認(rèn)證方式,在 Web 開發(fā)中的專業(yè)術(shù)語叫做 Cookie。
Cookie
Cookie 是存儲(chǔ)在用戶瀏覽器中的一段不超過 4 KB 的字符串。它由一個(gè)名稱(Name)、一個(gè)值(Value)和其它幾個(gè)用于控制 Cookie 有效期、安全性、使用范圍的可選屬性組成。不同域名下的 Cookie 各自獨(dú)立,每當(dāng)客戶端發(fā)起請求時(shí),會(huì)自動(dòng)把當(dāng)前域名下所有未過期的 Cookie 一同發(fā)送到服務(wù)器。
Cookie的幾大特性:
自動(dòng)發(fā)送域名獨(dú)立過期時(shí)限4KB 限制
Cookie 在身份認(rèn)證中的作用
客戶端第一次請求服務(wù)器的時(shí)候,服務(wù)器通過響應(yīng)頭的形式,向客戶端發(fā)送一個(gè)身份認(rèn)證的 Cookie,客戶端會(huì)自動(dòng)將 Cookie 保存在瀏覽器中。隨后,當(dāng)客戶端瀏覽器每次請求服務(wù)器的時(shí)候,瀏覽器會(huì)自動(dòng)將身份認(rèn)證相關(guān)的 Cookie,通過請求頭的形式發(fā)送給服務(wù)器,服務(wù)器即可驗(yàn)明客戶端的身份。
Cookie 不具有安全性
由于 Cookie 是存儲(chǔ)在瀏覽器中的,而且瀏覽器也提供了讀寫 Cookie 的 API,因此 Cookie 很容易被偽造,不具有安全性。因此不建議服務(wù)器將重要的隱私數(shù)據(jù),通過 Cookie 的形式發(fā)送給瀏覽器。注意:千萬不要使用 Cookie 存儲(chǔ)重要且隱私的數(shù)據(jù)!比如用戶的身份信息、密碼等。
6.3Session
會(huì)員卡 + 刷卡認(rèn)證”的設(shè)計(jì)理念,就是 Session 認(rèn)證機(jī)制的精髓
6.3.1安裝express-session 中間件
npm express 和 npm express-session
這里容易出現(xiàn)到錯(cuò)誤 :
靜態(tài)托管頁面找不到資源出現(xiàn) 開局導(dǎo)入path模塊 使用path.join拼接資源目錄80端口占用問題 修改端口號(hào)即可
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
const path = require('path');
// TODO_01:請配置 Session 中間件
// 托管靜態(tài)頁面
app.use(express.static(path.join(__dirname, './pages')));
// 解析 POST 提交過來的表單數(shù)據(jù)
app.use(express.urlencoded({ extended: false }))
// 登錄的 API 接口
app.post('/api/login', (req, res) => {
// 判斷用戶提交的登錄信息是否正確
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登錄失敗' })
}
// TODO_02:請將登錄成功后的用戶信息,保存到 Session 中
res.send({ status: 0, msg: '登錄成功' })
})
// 獲取用戶姓名的接口
app.get('/api/username', (req, res) => {
// TODO_03:請從 Session 中獲取用戶的名稱,響應(yīng)給客戶端
})
// 退出登錄的接口
app.post('/api/logout', (req, res) => {
// TODO_04:清空 Session 信息
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(81, function () {
console.log('Express server running at http://127.0.0.1:81')
})
局限性
Session 認(rèn)證機(jī)制需要配合 Cookie 才能實(shí)現(xiàn)。由于 Cookie 默認(rèn)不支持跨域訪問,所以,當(dāng)涉及到前端跨域請求后端接口的時(shí)候,需要做很多額外的配置,才能實(shí)現(xiàn)跨域 Session 認(rèn)證。
6.5WEB開發(fā)模塊2
JWT(英文全稱:JSON Web Token)是目前最流行的跨域認(rèn)證解決方案??偨Y(jié):用戶的信息通過 Token 字符串的形式,保存在客戶端瀏覽器中。服務(wù)器通過還原 Token 字符串的形式來認(rèn)證用戶的身份。
6.5.1組成部分
JWT 通常由三部分組成,分別是 Header(頭部)、Payload(有效荷載)、Signature(簽名)。三者之間使用英文的“.”分隔,格式如下:
Header.Payload.Signature
Payload 部分才是真正的用戶信息,它是用戶信息經(jīng)過加密之后生成的字符串。Header 和 Signature 是安全性相關(guān)的部分,只是為了保證 Token 的安全性。
6.5.2使用方法
客戶端收到服務(wù)器返回的 JWT 之后,通常會(huì)將它儲(chǔ)存在 localStorage 或 sessionStorage 中。此后,客戶端每次與服務(wù)器通信,都要帶上這個(gè) JWT 的字符串,從而進(jìn)行身份認(rèn)證。推薦的做法是把 JWT 放在 HTTP 請求頭的 Authorization 字段中,格式如下:
Authorization:Bearer
6.5.3安裝
npm install jsonwebtoken express-jwt
其中:
jsonwebtoken 用于生成 JWT 字符串express-jwt 用于將 JWT 字符串解析還原成 JSON 對象
6.5.4定義密鑰
為了保證 JWT 字符串的安全性,防止 JWT 字符串在網(wǎng)絡(luò)傳輸過程中被別人破解,我們需要專門定義一個(gè)用于加密和解密的 secret 密鑰:
當(dāng)生成 JWT 字符串的時(shí)候,需要使用 secret 密鑰對用戶的信息進(jìn)行加密,最終得到加密好的 JWT 字符串當(dāng)把 JWT 字符串解析還原成 JSON 對象的時(shí)候,需要使用 secret 密鑰進(jìn)行解密
const secretKey = 'itheima No1 ^_^'
將 JWT 字符串還原為 JSON 對象
客戶端每次在訪問那些有權(quán)限接口的時(shí)候,都需要主動(dòng)通過請求頭中的 Authorization 字段,將 Token 字符串發(fā)送到服務(wù)器進(jìn)行身份認(rèn)證。此時(shí),服務(wù)器可以通過 express-jwt 這個(gè)中間件,自動(dòng)將客戶端發(fā)送過來的 Token 解析還原成 JSON 對象:
使用 req.user 獲取用戶信息
.當(dāng) express-jwt 這個(gè)中間件配置成功之后,即可在那些有權(quán)限的接口中,使用 req.user 對象,來訪問從 JWT 字符串中解析出來的用戶信息了,示例代碼如下:
捕獲解析 JWT 失敗后產(chǎn)生的錯(cuò)誤
當(dāng)使用 express-jwt 解析 Token 字符串時(shí),如果客戶端發(fā)送過來的 Token 字符串過期或不合法,會(huì)產(chǎn)生一個(gè)解析失敗的錯(cuò)誤,影響項(xiàng)目的正常運(yùn)行。我們可以通過 Express 的錯(cuò)誤中間件,捕獲這個(gè)錯(cuò)誤并進(jìn)行相關(guān)的處理
整體:
// 導(dǎo)入 express 模塊
const express = require('express')
// 創(chuàng)建 express 的服務(wù)器實(shí)例
const app = express()
// TODO_01:安裝并導(dǎo)入 JWT 相關(guān)的兩個(gè)包,分別是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 允許跨域資源共享
const cors = require('cors')
app.use(cors())
// 解析 post 表單數(shù)據(jù)的中間件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
// TODO_02:定義 secret 密鑰,建議將密鑰命名為 secretKey
const secretKey = 'itheima No1 ^_^'
// TODO_04:注冊將 JWT 字符串解析還原成 JSON 對象的中間件
// 注意:只要配置成功了 express-jwt 這個(gè)中間件,就可以把解析出來的用戶信息,掛載到 req.user 屬性上
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
// 登錄接口
app.post('/api/login', function (req, res) {
// 將 req.body 請求體中的數(shù)據(jù),轉(zhuǎn)存為 userinfo 常量
const userinfo = req.body
// 登錄失敗
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({
status: 400,
message: '登錄失?。?,
})
}
// 登錄成功
// TODO_03:在登錄成功之后,調(diào)用 jwt.sign() 方法生成 JWT 字符串。并通過 token 屬性發(fā)送給客戶端
// 參數(shù)1:用戶的信息對象
// 參數(shù)2:加密的秘鑰
// 參數(shù)3:配置對象,可以配置當(dāng)前 token 的有效期
// 記?。呵f不要把密碼加密到 token 字符中s
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '100000s' })
res.send({
status: 200,
message: '登錄成功!',
token: tokenStr, // 要發(fā)送給客戶端的 token 字符串
})
})
// 這是一個(gè)有權(quán)限的 API 接口
app.get('/admin/getinfo', function (req, res) {
// TODO_05:使用 req.user 獲取用戶信息,并使用 data 屬性將用戶信息發(fā)送給客戶端
console.log(req.user)
res.send({
status: 200,
message: '獲取用戶信息成功!',
data: req.user, // 要發(fā)送給客戶端的用戶信息
})
})
// TODO_06:使用全局錯(cuò)誤處理中間件,捕獲解析 JWT 失敗后產(chǎn)生的錯(cuò)誤
app.use((err, req, res, next) => {
// 這次錯(cuò)誤是由 token 解析失敗導(dǎo)致的
if (err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '無效的token',
})
}
res.send({
status: 500,
message: '未知的錯(cuò)誤',
})
})
// 調(diào)用 app.listen 方法,指定端口號(hào)并啟動(dòng)web服務(wù)器
app.listen(8888, function () {
console.log('Express server running at http://127.0.0.1:8888')
})
柚子快報(bào)激活碼778899分享:Node.js
好文鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。