欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)激活碼778899分享:Node.js

柚子快報(bào)激活碼778899分享:Node.js

http://yzkb.51969.com/

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

http://yzkb.51969.com/

好文鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://m.gantiao.com.cn/post/19256714.html

發(fā)布評論

您暫未設(shè)置收款碼

請?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄