1. 程式人生 > 實用技巧 >08【Express框架】詳細版 - Express框架簡介,中介軟體, Express請求處理,express-art-template模板引擎.

08【Express框架】詳細版 - Express框架簡介,中介軟體, Express請求處理,express-art-template模板引擎.

Express框架

目標

  1. 能夠使用Express建立web伺服器
  2. 能夠使用Express處理請求引數
  3. 能夠使用Express處理靜態資源
  4. 能夠使用中介軟體處理請求
  5. 能夠在Express中整合art-template模板引擎
  6. Express框架簡介及初體驗
  7. Express框架請求處理
  8. express-art-template模板引擎
  9. Express中介軟體

1. Express框架簡介及初體驗

1.1 Express框架是什麼?

1)Express是一個基於Node平臺的web應用開發框架,它提供了一系列的強大特性,幫助你建立各種Web網站應用。使用原生JS程式碼寫起來比較複雜,比較底層的;比如實現路由功能,需要對請求地址進行解析,還要進行各種判斷,程式碼亂,不易閱讀;再比如,實現靜態資源訪問功能

,還要使用檔案讀取模組,對檔案內容讀取,還要設計響應內容型別,但實際和網站本身的業務邏輯沒有關係;還有,接收post請求引數的程式碼,需要對請求物件新增事件,手動拼接請求引數,對請求引數的格式進行轉化,都是複雜的,並且還是和和業務邏輯沒有關係;原生JS實現網站應用比較困難,express就出現了

2)這個是nodejs的第三方模組-使用 npm install express 命令進行下載。企業中建立web應用的標準

1.2 Express框架特性

  • 提供了方便簡潔的路由定義方式:router第三方模組 ,其實就是從express框架中抽取出來的
  • 對獲取HTTP請求引數進行了簡化處理:不用再轉換格式,直接拿到物件型別
  • 模板引擎 支援程度高,方便渲染動態HTML頁面;
  • 提供了中介軟體(對請求的攔截)機制有效控制HTTP請求
  • 擁有大量第三方中介軟體 對功能進行擴充套件:非常少的程式碼,做同樣的事情

1.3 原生Node.js與Express框架對比 之 路由

app.on('request', (req, res) => {

// 獲取客戶端的請求路徑

let { pathname } = url.parse(req.url);

// 對請求路徑進行判斷 不同的路徑地址響應不同的內容

if (pathname == '/' || pathname == 'index') {

res.end('歡迎來到首頁

');

} else if (pathname == '/list') {

res.end('歡迎來到列表頁');

} else if (pathname == '/about') {

res.end('歡迎來到關於我們頁面')

} else {

res.end('抱歉, 您訪問的頁面出遊了');

}

});

// 當客戶端以get方式訪問/

app.get('/', (req, res) => {

// 對客戶端做出響應

res.send('Hello Express');

});

// 當客戶端以post方式訪問/add路由時

app.post('/add', (req, res) => {

res.send('使用post方式請求了/add路由');

});

1.4 原生Node.js與Express框架對比 之 引數

app.on('request', (req, res) => {

// 獲取GET引數

let {query} = url.parse(req.url, true);

// 獲取POST引數

let postData = '';

req.on('data', (chunk) => {

postData += chunk;

});

req.on('end', () => {

console.log(querystring.parse(postData)

}));

});

app.get('/', (req, res) => {

// 獲取GET引數req.query

console.log(req.query);

});

app.post('/', (req, res) => {

// 獲取POST引數 req.body

console.log(req.body);

})

1.5 Express初體驗

使用Express框架 建立web伺服器及其簡單,呼叫express模組 返回的函式即可

命令列下載 npm install express

//1, 引入Express框架,返回值是一個方法,通過呼叫這個方法,就可以建立網站伺服器,就不用HTTP模組和呼叫createserver()方法

const express = require('express');

//2, 使用框架建立web伺服器 + 監聽埠 = 向外提供服務

const app = express();

// 4, 當客戶端以get方式訪問/路由時, 伺服器要建立路由來響應客戶端的請求;如何建立路由,和第三方模組 router是一樣的 app.get(‘預設訪問地址/’,請求處理函式,兩個引數分別為 請求物件和 響應物件) 用來接受 get 請求

app.get('/', (req, res) => {

// 不再是res.end(), 對客戶端做出響應 send()方法會根據內容的型別自動設定請求頭

// send() 內部回檢測響應內容的型別;會自動設定HTTP狀態碼;會幫我們自動設定響應的內容型別以及 編碼

res.send('Hello Express'); // <h2>Hello Express</h2> {say: 'hello'}

});

//在定義一個路由,當訪問 / list 的時候,響應一個其他內容;send 內部 還可以傳遞Json 物件

app.get('/list', (req, res) => {

res.send({name: '', age: 20})

})

//3, 程式監聽3000

app.listen(3000);

2. 中介軟體

2.1 什麼是中介軟體

1) 中介軟體就是 一堆方法可以 接收客戶端發來的請求可以對請求做出響應也可以將請求繼續交給下一個中介軟體繼續處理。專門接受請求處理請求的

下圖:中介軟體處理請求的過程:

圖兩邊是當客戶端瀏覽器, 向中間的伺服器傳送請求

當瀏覽器傳送請求,伺服器可以使用中介軟體 接受這個請求進行處理,直接對客戶端做出響應,或者交給下一個中介軟體繼續處理,有下一個 中介軟體對客戶端瀏覽器做出相應;

中介軟體好處:可以對複雜的請求處理邏輯,進行分開處理,也可以再請求到到指定路由之前做一些驗證:比如檢視使用者是不是登入,如果登入,在向下繼續執行

2) 中介軟體主要由兩部分構成,中介軟體方法以及請求處理函式中介軟體方法 由Express框架提供,負責攔截請求請求處理函式 由開發人員提供,負責處理請求

app.get('請求路徑', '處理函式') // 接收並處理get請求

app.post('請求路徑', '處理函式') // 接收並處理post請求

3) 可以針對同一個請求設定 多箇中間件對同一個請求進行多次處理

預設情況下,請求從上到下依次匹配中介軟體,一旦匹配成功,終止匹配

可以呼叫第三個引數next()方法將請求的控制權交給下一個中介軟體,直到遇到結束請求的中介軟體

app.get('/request', (req, res, next) => {

req.name = "zhangsan";

next();

});

app.get('/request', (req, res) => {

res.send(req.name);

});

2.2 app.use中介軟體用法

- app.use 匹配所有的請求方式可以直接傳入請求處理函式代表接收所有的請求,只要客戶端發來請求,就可以匹配到當前中介軟體

中介軟體是有順序的,所以,中介軟體必須定義在其他前邊;否則其他中介軟體匹配到了這個請求,有沒有將權力交給下一個中介軟體,也是匹配不到這個中介軟體的

app.use((req, res, next) => { console.log(req.url); next(); });

  • app.use 第一個引數 也可以傳入請求地址,代表不論什麼請求方式,只要是這個請求地址就接收這個請求。

app.use('/admin', (req, res, next) => { console.log(req.url); next(); });

2.3 中介軟體應用

1. 路由保護,客戶端在訪問 需要登入的頁面時可以先使用中介軟體判斷使用者登入狀態,使用者如果未登入,則攔截請求,直接響應, 禁止使用者進入需要登入的頁面

// 定義一個路由:要訪問必須要先登入可以的

2. 網站維護公告在所有路由的最上面定義接收所有請求的中介軟體,直接為客戶端做出響應,網站正在維護中。

// 網站公告,比如在凌晨 6:00 -12:00 要維護,不想要使用者訪問這個網站,要定義在所有的路由的前邊,沒有呼叫 next(), 請求到這裡就截止了

app.use((req, res, next) => {

res.send('當前網站正在維護,請在其他時間段訪問...')

})

3. 自定義404頁面,使用者訪問路徑不存在時,同時使用者訪問的頁面不存在,當所有使用者訪問的上邊所有的路由不存在,才會響應給使用者,所以定義在所有路由最後邊,不會呼叫next();status404)更改狀態碼,為客戶端響應404狀態碼以及提示資訊

2.4 錯誤處理中介軟體

在程式執行的過程中,不可避免的會出現一些無法預料的錯誤,比如檔案讀取失敗資料庫連線失敗,錯誤處理中介軟體是一個集中處理錯誤的地方。

想要出錯以後,還能繼續執行,需要捕獲錯誤,加入錯誤處理;程式錯誤分為兩種:

  1. 應用邏輯錯誤:=bug 開發階段解決
  2. 讀取硬碟檔案/資料庫連線錯誤:無法開發階段預料到,在執行過程中,需要被捕獲和妥善處理;

如何處理錯誤呢?在每一個會出錯的地方進行判斷,但是程式碼太多!所以提供了錯誤處理中介軟體;

只能捕獲到 同步程式碼錯誤!!!非同步需要手動觸發呼叫next(0方法 當非同步程式出現錯誤時,呼叫next()方法,並且將錯誤資訊通過引數的形式傳遞給next()方法,即可觸發錯誤處理中介軟體

有四個引數,發生錯誤,自動執行錯誤處理中介軟體

app.use((err, req, res, next) => {

res.status(500).send('伺服器發生未知錯誤');

})

如果檔案讀取錯誤,系統會把錯誤資訊通過引數傳給我們,我們對錯誤物件進行判斷,如果它真的時錯誤物件,不是null,就呼叫next()方法,傳給他,他就會觸發錯誤處理中介軟體了

app.get("/", (req, res, next) => {

fs.readFile("/file-does-not-exist", (err, data) => {

if (err) {

next(err);

}

});

});

//05js 錯誤處理中介軟體

// 引入express框架 + 檔案讀取模組

const express = require('express');

const fs = require('fs');

// 建立網站伺服器

const app = express();

// 普通的路由中介軟體

app.get('/index', (req, res, next) => {

// throw new Error('程式發生了未知錯誤') 丟擲錯誤 ,不報錯往下執行

fs.readFile('./01.js', 'utf8', (err, result) => {

if (err != null) {

next(err) // 傳引數,代表觸發中介軟體,不傳引數,代表控制權交給下一個

}else {

res.send(result)

}

})

// res.send('程式正常執行')

})

// 錯誤處理中間

app.use((err, req, res, next) => {

res.status(500).send(err.message);

})

// 監聽埠 

app.listen(3000);

console.log('網站伺服器啟動成功');

2.5 捕獲錯誤

node.js非同步API的錯誤資訊都是通過 回撥函式 獲取的支援Promise物件的非同步API 發生錯誤可以通過catch方法捕獲 非同步函式執行如果發生錯誤要如何捕獲錯誤呢

try catch 可以捕獲 非同步函式以及其他同步程式碼 在執行過程中發生的錯誤,但是 不能捕獲其他型別的API發生的錯誤

app.get("/", async (req, res, next) => {

// 如果程式沒有錯誤,跳到trycatch外;如果程式有錯誤,會執行catch裡邊的程式碼,裡邊的引數就是錯誤資訊,可以呼叫next方法,手動觸發錯誤處理中介軟體;try()裡邊的程式碼,是從資料塊中查詢資料,如果查詢失敗,就跳轉catch,執行並將錯誤資訊傳給錯誤處理中介軟體;

try {

await User.find({name: '張三'})

}catch(ex) {

next(ex); // 呼叫next() 觸發錯誤處理中介軟體

} });

命令列已經不報錯了,程式就可以繼續執行;增加了我們程式的健壯性

3. Express請求處理

3.1 構建模組化路由

雖然已經可以通過 app.get(), app.post()方法建立路由了,但是在一般情況下,路由的數量是非常多的,如果將所有的放在同一檔案中,非常可怕,所以express提供了模組化,進行分類,不同的型別路由放在不同的模組中,方便管理。

例如:部落格網站:使用者看的文章列表,詳情頁面;管理員看的文章釋出,管理頁面等。設定不同的路由進行分別管理

const express = require('express') // 引入框架,返回express方法,直接呼叫或者使用他下邊的其他方法;比如 express.Router用來建立路由

const home = express.Router();// 建立路由物件

app.use('/home', home); // 將路由和請求路徑進行匹配;當客戶端訪問什麼請求路徑時/home'才能使用當前路由來處理,用app.use()方法來匹配 home;程式碼中並沒有請求處理函式,請求來了以後,在哪裡處理呢?具體請求處理再二級路由中完成

home.get('/index', () => {// home路由下的get()方法繼續建立路由,訪問:/home/index 二級路由

res.send('歡迎來到部落格展示頁面'); // /home/index

});

3.2 構建模組化路由

// home.js

// admin,js

// app.js

3.3 GET引數的獲取

Express框架中使用 req.query 即可獲取 GET引數 query 屬性下存的就是 get請求引數,不在需要引入url 模組,通過對請求地址進行解析來獲取get 請求引數了;框架內部會將GET引數 轉換為 物件並返回

// 接收位址列中問號後面的引數,客戶端訪問時加了請求引數 name=zhangsan&age=30

// 例如: http://localhost:3000/?name=zhangsan&age=30

app.get('/', (req, res) => {

console.log(req.query); // 直接通過 req.query 就可以拿到?號後的請求引數了,並且已經解析成物件型別{"name": "zhangsan", "age": "30"} });

3.4 POST引數的獲取

Express中接收 post請求引數 需要藉助第三方包 body-parser。Npm i body-parser下載

const bodyParser = require('body-parser'); // 引入body-parser模組

app.use(bodyParser.urlencoded({ extended: false }));// 配置body-parser模組;使用app.use()這個中介軟體攔截所有請求,呼叫'body-parser'模組下邊的 urlencoded() 方法,對請求進行處理,方法內部會檢測當前請求中是不是包含請求引數,如果包含,就接受並轉換為物件型別;然後在為req這個請求新增一個屬性,屬性的名字叫body;並且將引數作為值賦值給 req.body屬性,最後呼叫next()將請求控制權交給下一個中介軟體

app.post('/add', (req, res) => { // 接收請求

console.log(req.body); // 接收post請求引數

})

10.js 如何獲取post請求引數

如何傳送 post 請求,通過表單就可以:post.html

點選提交後,就提交到 /add這個路由地址去了:

11.js app.use方法

3.5 Express 路由引數

傳遞和接受 get 請求引數,還有另一種方式; 路由引數;可以讓請求地址看起來美觀,路由程式碼容易閱讀;更容易看出傳了那些引數;:id 是一個佔位符,請求當前路由,要傳遞一個id 作為引數,不是實際的引數

app.get('/find/:id', (req, res) => {

console.log(req.params); // {id: 123} req.params 獲取引數

});

localhost:3000/find/123 // 請求引數/id = 123

3.6 靜態資源的處理

通過Express內建的 express.static可以方便地託管靜態檔案,例如imgCSSJavaScript 檔案等。

app.use(express.static('public')); express.static(‘引數:靜態資源存放的目錄’);呼叫傳給app.use()中介軟體,攔截所有請求,將請求交給express.static()這個方法處理,並且將靜態資源目錄告訴express.static()方法;方法內部判斷客戶端發來的請求,如果是靜態資源請求,直接響應給客戶端,終止請求;如果不是再方法內部呼叫next()將請求控制權交給下一個中介軟體開啟靜態資源訪問功能後,就可以public 目錄下面的檔案就可以通過以下方式訪問了

訪問:http://localhost:3000/static/images/1.jpg

4. express-art-template模板引擎

4.1 模板引擎

  1. 為了使 art-template模板引擎 能夠更好的和 Express框架配合,模板引擎官方在原art-template模板引擎的基礎上封裝了express-art-template
  2. 使用 npm install art-template express-art-template 命令進行安裝。

// 告訴express 框架,使用的模板引擎是什麼? 當渲染字尾為art的模板時 使用express-art-template

app.engine('art', require('express-art-template'));

// 設定模板存放目錄 app.setexpress框架進行配置

app.set('views', path.join(__dirname, 'views'));

// 渲染模板時不寫字尾 預設拼接art字尾

app.set('view engine', 'art');

app.get('/', (req, res) => {

// 渲染模板

res.render('index');

});

4.2 app.locals 物件

不同的頁面中,總會有公共資料程式碼中如何查詢公共資料呢

  • 在不同頁面路由中 都去查詢這個相同的資料,render 將資料填充到模板中,麻煩; 一次,所有能用到都而已拿到這個數據呢?
  • 將變數設定到 app.locals 物件下面,這個資料在所有的模板中都可以獲取到。!!!

app.locals.users = [{

name: '張三',

age: 20

},{

name: '李四',

age: 20

}]