国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Express 實戰(zhàn)(四):中間件

mochixuan / 1685人閱讀

摘要:調(diào)用函數(shù)執(zhí)行下一個中間件函數(shù)。然后,該中間件調(diào)用函數(shù)檢查文件是否存在。為了代碼更加清晰,你也可以將代碼改寫為另外,這里在調(diào)用函數(shù)是使用的是作為輸出選項。事實上,中間件有兩種類型。

原生 Node 的單一請求處理函數(shù),隨著功能的擴(kuò)張勢必會變的越來越難以維護(hù)。而 Express 框架則可以通過中間件的方式按照模塊和功能對處理函數(shù)進(jìn)行切割處理。這樣拆分后的模塊不僅邏輯清晰,更重要的是對后期維護(hù)和開發(fā)非常有利。

本文將會詳細(xì)介紹 Express 的使用,其中主要內(nèi)容包括:

中間件是什么?

中間件棧以及請求處理的工作流。

中間件的使用。

如何實現(xiàn)自己的中間件。

Express 中常用的第三方中間件。

希望在讀完本文后,你能對這個 Express 最主要的構(gòu)成有更加清晰的認(rèn)知。

中間件和中間件棧

對所有的 Web 應(yīng)用來說它的處理流程可以簡單描述為:監(jiān)聽請求、解析請求、做出響應(yīng)。當(dāng)然,Node 也遵循這一套流程,只不過將那些請求都轉(zhuǎn)化為了 JavaScript 對象。

與原生 Node 代碼不同的是,Express 會將上圖中的最后一部分拆分為一組中間件函數(shù)(中間件棧)。所以Express 的工作流大致如下:

與純 Node 不同的是,Express 中的中間件棧函數(shù)中除了表示請求和響應(yīng)的參數(shù)外,還添加了第三個參數(shù)。該參數(shù)是一個函數(shù)對象,按照慣例我們稱之為 next 。它用于傳遞中間件棧對某個請求的處理流。

在整個中間件棧的處理流中,最少有一個函數(shù)需要調(diào)用 res.end 方法結(jié)束響應(yīng)處理。下面我們就通過搭建靜態(tài)文件服務(wù)來加深對中間件棧的理解。

示例:一個靜態(tài)文件服務(wù)器

創(chuàng)建一個文件夾并為此提供靜態(tài)文件服務(wù)。你可以在文件夾中存放任何文件,例如:HTML 文件、圖片。最終所有的這些文件都能通過示例程序進(jìn)行網(wǎng)絡(luò)訪問。

該示例程序的功能大致包括:能夠正確返回存在的文件;文件不存在時返回 404 錯誤;打印所有的訪問請求。所以,該示例的中間件棧如下:

日志記錄中間件。該函數(shù)會在終端打印所有的網(wǎng)絡(luò)請求,并在打印介紹后繼續(xù)下一個中間件函數(shù)。

靜態(tài)文件發(fā)送中間件。如果訪問的文件存在則返回給客戶端。如果文件不存在則會跳到錯誤處理中間件。

404 處理中間件。如果文件不存在的話,該中間件將會給客戶端發(fā)送 404 錯誤信息。

流程圖如下:

明確示例的目標(biāo)和需求后,下面我們就進(jìn)行代碼實現(xiàn)。

準(zhǔn)備工作

與之前一樣,新建工程目錄并將下面內(nèi)容復(fù)制到 package.json 中:

{
    "name": "static-file-fun", 
    "private": true, 
    "scripts": {
        "start": "node app.js" 
    }
}

接下來,我們執(zhí)行 npm install express --save 安裝最新版 Express 。確保安裝完成后,我們在工程目錄里新建文件夾 static 并在其中存放一些文件。最后,我們新建工程主入口文件 app.js 。一切就緒后,工程的大致目錄如下:

另外,值的一提的是之所以配置 npm start 命令,既是因為開發(fā)約定更重要的是讓其他人開箱即用無需自己手動查找程序入口。

第一個中間件:日志記錄

按照前面制訂的處理流程,首先需要實現(xiàn)的就是日志中間件。復(fù)制下面代碼到入口文件 app.js 中:

var express = require("express");
var path = require("path");
var fs = require("fs");

var app = express();

app.use(function(req, res, next) {
    console.log("Request IP: " + req.url);
    console.log("Request date: " + new Date());
});

app.listen(3000, function() {
    console.log("App started on port 3000");
});

通過上面的 app.use 函數(shù),我們成功實現(xiàn)了應(yīng)用中的第一個功能,即記錄每次網(wǎng)絡(luò)請求。當(dāng)然這里還有一個問題,當(dāng)前應(yīng)用并不會對請求做出響應(yīng)。這意味這:如果你用 npm start 拉起服務(wù)并訪問 loaclhost:3000 瀏覽器會一直掛起等待響應(yīng)直到出現(xiàn)超時錯誤。不過不要擔(dān)心,等補(bǔ)全后面功能后我們就可以在該中間件調(diào)用 next() 將響應(yīng)的任務(wù)交給后續(xù)中間件。

這里我們只需要明白:理論上一個中間件函數(shù)處理結(jié)束后,它必須執(zhí)行以下兩個步驟中的一個。

所有處理結(jié)束,發(fā)送 red.end 或者 Express 中的 red.sendFile 等函數(shù)結(jié)束響應(yīng)。

調(diào)用 next 函數(shù)執(zhí)行下一個中間件函數(shù)。

所以這里我們先把 next() 調(diào)用補(bǔ)全將日志中間件的邏輯理順:

// ...
app.use(function(req, res, next) {
    console.log("Request IP: " + req.url);
    console.log("Request date: " + new Date());
    next(); // 新的這行很重要
});
// ...

此時重啟服務(wù)并訪問 http://localhost:3000 的話訪問請求會被完整記錄下來。但是因為程序沒有做出響應(yīng) ,Express 任會給客戶端發(fā)送一個錯誤信息。所以,接下來我們就補(bǔ)全后續(xù)流程。

靜態(tài)文件服務(wù)中間件

靜態(tài)文件服務(wù)中間件應(yīng)該有以下幾個功能:

檢查目錄中是否存在該文件

如果文件存在則調(diào)用 res.sendFile 結(jié)束響應(yīng)處理。

如果文件不存在則繼續(xù)調(diào)用下一個中間件從代碼角度來說就是調(diào)用 next

其中我們需要使用內(nèi)置的 path 模塊指定路徑,然后使用內(nèi)置的 fs 模塊判斷文件釋放存在。將下面代碼添加到日志中間件后面:

// 日志中間件
app.use(function(req, res, next) {
? // …
});
?
app.use(function(req, res, next) {
? var filePath = path.join(__dirname, "static", req.url);? 
? fs.exists(filePath, function(exists) {????????????????????? 
??? if (exists) {???? ????????????????????????????????????????
????? res.sendFile(filePath);???????????????????????????????? 
??? } else {????????????????????????????????????????????????? 
????? next();????????????????????????????????????????????????
??? }
? });
});
?
app.listen(3000, function() {
    ...
}

在中間件中我們首先使用 path.join 拼接文件完整路徑。例如,如果用戶訪問 http://localhost:3000/celine.... 文件的話 req.url 的值就是 /celine.mp3 拼接后的完整路徑就是 "/path/to/your/project/static/celine.mp3" 了。

然后,該中間件調(diào)用 fs.exists 函數(shù)檢查文件是否存在。如果文件存在則發(fā)生文件,否則調(diào)用 next() 繼續(xù)執(zhí)行下一個中間件。而如果訪問的 URL 沒有對應(yīng)的文件的話就會出現(xiàn)之前一樣的錯誤。所以下面需要實現(xiàn)最后一個中間件:404 處理中間件。

404 處理中間件

404 中間件的任務(wù)就是發(fā)送 404 錯誤信息,復(fù)制下面的實現(xiàn)代碼并添加到靜態(tài)服務(wù)中間件后面:


app.use(function(req, res) {
    // 設(shè)置狀態(tài)碼為404
    res.status(404);
    // 發(fā)送錯誤提示
    res.send("File not found!");
});

// ...

這樣整個工程就算完成了。如果你再次啟動服務(wù)的話,之前的錯誤就會被一個 404 錯誤取代。另外,如果你將該中間件函數(shù)移動到中間件棧的第一個,那么你會發(fā)現(xiàn)所有的請求都會得到 404 的錯誤信息。這意味著中間件棧中的函數(shù)順序是非常重要的。

到這里,app.js 中的完整代碼如下:

var express = require("express");
var path = require("path");
var fs = require("fs");
var app = express();
app.use(function(req, res, next) {
    console.log("Request IP: " + req.url);
    console.log("Request date: " + new Date());
    next();
});
app.use(function(req, res, next) {
    var filePath = path.join(__dirname, "static", req.url);
    fs.stat(filePath, function(err, fileInfo) {
        if (err) {
            next();
            return;
        }
        if (fileInfo.isFile()) {
            res.sendFile(filePath);
        } else {
            next();
        }
    });
});
app.use(function(req, res) {
    res.status(404);
    res.send("File not found!");
});
app.listen(3000, function() {
    console.log("App started on port 3000");
});

當(dāng)然,這只是初步的代碼,還有很多地方可以進(jìn)行優(yōu)化。

將日志中間件替換為:Morgan

在軟件開發(fā)中如果你的問題已經(jīng)存在比較好的解決方案,那么理想的做法是直接使用該方案而不應(yīng)該“重復(fù)造輪子”。所以,下面我們使用功能強(qiáng)大的 Morgan 替換掉上面自己實現(xiàn)的日志中間件。雖然,該中間件不是 Express 內(nèi)置模塊,但是它卻是由 Express 團(tuán)隊維護(hù)并久經(jīng)考驗。

運(yùn)行 npm install morgan --save 安裝最新版本的 Morgan 模塊。然后使用 Morgan 替換掉之前的日志中間件:

var express = require("express");
var morgan = require("morgan");
...

var app = express();
app.use(morgan("short"));

...

當(dāng)你再次啟動服務(wù)并訪問資源時,終端將會打印包括 IP 地址在內(nèi)的有用信息:

代碼中 morgan 其是一個函數(shù)并且它的返回值是一個中間件函數(shù)。當(dāng)你調(diào)用它的時候,它會返回一個類似之間實現(xiàn)的日志中間件。為了代碼更加清晰,你也可以將代碼改寫為:

var morganMiddleware = morgan("short");
app.use(morganMiddleware);

另外,這里在調(diào)用函數(shù)是使用的是 short 作為輸出選項。其實該模塊還提供另兩個輸出選項:combined 打印最多信息;tiny 打印最少的信息。

除了使用 Morgan 替換原有日志中間件之外,我們還可以使用內(nèi)置的靜態(tài)中間件替換之前的代碼實現(xiàn)。

使用 Express 內(nèi)置靜態(tài)文件中間件

接下來,我們使用 Express 內(nèi)置的 express.static 模塊來替換之前的靜態(tài)文件中間件。它的工作原理與之前的中間件代碼類似,但是它具有更好的安全性和性能。例如,它在內(nèi)部實現(xiàn)了資源的緩存功能。

與 Morgan 一樣,express.static 函數(shù)的返回值也是一個中間件函數(shù)。我們只需為 express.static 函數(shù)指定路徑參數(shù)即可。代碼如下:

var staticPath = path.join(__dirname, "static"); // 設(shè)置靜態(tài)文件的路徑
app.use(express.static(staticPath)); // 使用express.static從靜態(tài)路徑提供服務(wù)
// ...

完成替換后你會發(fā)現(xiàn)代碼相較之前明顯變的簡練了,與此同時功能反而比之前更強(qiáng)。另外,這些久經(jīng)考驗的中間件模塊遠(yuǎn)比自己的代碼實現(xiàn)功能更多也更可靠。此時 app.js 中的完整代碼:

var express = require("express");
var morgan = require("morgan");
var path = require("path");
var app = express();
app.use(morgan("short"));
var staticPath = path.join(__dirname, "static");
app.use(express.static(staticPath));
app.use(function(req, res) {
    res.status(404);
    res.send("File not found!");
});
app.listen(3000, function() {
    console.log("App started on port 3000");
});
錯誤處理中間件

之前我說過調(diào)用 next() 會按序執(zhí)行下一個中間件。其實,真實情況并不是這么簡單。事實上,Express 中間件有兩種類型。

到目前為止,你已經(jīng)接觸了第一種類型:包含三個參數(shù)的常規(guī)中間件函數(shù)(有時 next 會被忽略而只保留兩個參數(shù)),而絕大多數(shù)時候程序中都是使用這種常規(guī)模式。

第二種類型非常少見:錯誤處理中間件。當(dāng)你的 app 處于錯誤模式時,所有的常規(guī)中間件都會被跳過而直接執(zhí)行 Express 錯誤處理中間件。想要進(jìn)入錯誤模式,只需在調(diào)用 next 時附帶一個參數(shù)。這是調(diào)用錯誤對象的一種慣例,例如:next(new Error("Something bad happened!"))

錯誤處理中間件中需要四個參數(shù),其中后面三個和常規(guī)形式的一致而第一個參數(shù)則是 next(new Error("Something bad happened!")) 中傳遞過來的 Error 對象。你可以像使用常規(guī)中間件一樣來使用錯誤處理中間件,例如:調(diào)用 res.end 或者 next 。如果調(diào)用含參數(shù)的 next 中間件會繼續(xù)下一個錯誤處理中間件否則將會退出錯誤模式并調(diào)用下一個常規(guī)中間件。

假設(shè),現(xiàn)在有四個中間件依次排開,其中第三個為錯誤處理中間件而其他的都是常規(guī)中間件。如果沒有出現(xiàn)錯誤的話,流程應(yīng)該是:

如上所示,當(dāng)沒有錯誤發(fā)生時錯誤處理中間件就像不存在一樣。但是,一旦出現(xiàn)錯誤所有的常規(guī)中間件都被跳過,那么處理流程就會是這樣:

雖然 Express 沒有做出強(qiáng)制規(guī)定,但是一般錯誤處理中間件都會放在中間件棧的最下面。這樣所有之前的常規(guī)中間件發(fā)生錯誤時都會被該錯誤處理中間件所捕獲。

Express 的錯誤處理中間件只會捕獲由 next 觸發(fā)的錯誤,對于 throw 關(guān)鍵字觸發(fā)的異常則不在處理范圍內(nèi)。對于這些異常 Express 有自己的保護(hù)機(jī)制,當(dāng)請求失敗時 app 會返回一個 500 錯誤并且整個服務(wù)依舊在持續(xù)運(yùn)行。然而,對于語法錯誤這類異常將會直接導(dǎo)致服務(wù)奔潰。

現(xiàn)在通過一個簡單示例來看看 Express 中的錯誤處理中間件。假設(shè)該應(yīng)用對于用戶的任何請求都是通過 res.sendFile 發(fā)生圖片給用戶。代碼如下:

var express = require("express");
var path = require("path");
var app = express();

var filePath = path.join(__dirname, "celine.jpg");
app.use(function(req, res) {
  res.sendFile(filePath);
});
app.listen(3000, function() {
  console.log("App started on port 3000");
});

可以看到這是之前靜態(tài)文件服務(wù)的簡化版,對于任意請求都會發(fā)生 celine.jpg 圖片。但是如果該文件不存在,或者是文件讀取過程發(fā)生了錯誤該怎么辦呢?這就需要一些機(jī)制來處理這種異常錯誤了,而這正是錯誤處理中間件存在的理由。

為了觸發(fā)異常處理,我們在 res.sendFile 將異常回調(diào)函數(shù)補(bǔ)充完整。這個回調(diào)函數(shù)將會在文件發(fā)送之后得到執(zhí)行并且該回調(diào)函數(shù)中有一個參數(shù)標(biāo)記文件發(fā)送成功與否。代碼示例如下:

res.sendFile(filePath, function(err) {
  if (err) {
    console.error("File failed to send.");
  } else {
    console.log("File sent!");
  }
});

當(dāng)然,除了打印錯誤信息之外,我們還可以通過觸發(fā)異常進(jìn)入錯誤處理中間件函數(shù),而該部分代碼實現(xiàn)如下:

// ...
app.use(function(req, res, next) {
  res.sendFile(filePath, function(err) {
    if (err) {
      next(new Error("Error sending file!"));
    }
  });
});
// ...

異常觸發(fā)后接下來就是錯誤處理中間件的實現(xiàn)了。

通常情況下我們都會首先將錯誤信息記錄下來,而這些信息一般也不會展示給用戶。畢竟將一長段的 JavaScript 棧調(diào)用信息展示給不懂技術(shù)的用戶會給他們造成不必要的困惑。尤其是這些信息一旦暴露給了黑客,他們有可能就能逆向分析出網(wǎng)站是如何工作的從而造成信息風(fēng)險。

下面,我們僅僅在處理處理中間件中打印錯誤信息而不做任何進(jìn)一步的處理。它與之前的中間件類似只不過這里打印錯誤信息而不是請求信息。將下面代碼復(fù)制到所有常規(guī)中間件的后面:

// ...

app.use(function(err, req, res, next) {
    // 記錄錯誤
    console.error(err);
    // 繼續(xù)到下一個錯誤處理中間件
    next(err);
});
// ... 

現(xiàn)在,當(dāng)程序出現(xiàn)異常之后這些錯誤信息都將會被記錄在控制臺以便后面的進(jìn)一步分析。當(dāng)然,這里還有一些事情需要處理,例如:對請求作出響應(yīng)。將下面代碼放在上一個中間件之后:

// ...

app.use(function(err, req, res, next) {
  // 設(shè)置狀態(tài)碼為500
  res.status(500);
  // 發(fā)送錯誤信息
  res.send("Internal server error.");
});
// ...

請記住,這些錯誤處理中間件不管所在位置如何它都只能通過帶參 next 進(jìn)行觸發(fā)。對于這個簡單應(yīng)用來說可能沒有那么多異常和錯誤會觸發(fā)錯誤處理中間件。但是隨著應(yīng)用的擴(kuò)張,你就需要對錯誤行為進(jìn)行仔細(xì)測試。如果發(fā)生了異常,那么你應(yīng)該對妥善的處理好這些異常而不是讓程序崩潰。

總結(jié)

在本文中我們仔細(xì)探討了 Express 的核心模塊:中間件。其中的內(nèi)容包括:

Express 中間件棧的概念以及工作流。

如何編寫自定義的中間件函數(shù)。

如何編寫錯誤處理中間件。

常見中間件模塊的使用。

原文地址

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/84854.html

相關(guān)文章

  • 實戰(zhàn)】用 express+MongoDB 搭建一個完整的前端項目

    摘要:前言要做一個全沾的工程師,對于后端和數(shù)據(jù)庫來說,即使不認(rèn)識也要見個面的。基本了解的概念就好,主要是安裝上數(shù)據(jù)庫,并進(jìn)行簡單的增刪操作。 前言:要做一個全沾的工程師,對于后端和數(shù)據(jù)庫來說,即使不認(rèn)識也要見個面的。本文給的例子很簡單,也貼出來源碼,只要一步步下來,就可以跑起來啦~~~ 思考一個需求:做一個登錄頁面,自己搭建服務(wù)和數(shù)據(jù)庫,將用戶輸入的登錄信息保存到數(shù)據(jù)庫如何完成呢:首先選擇...

    Steve_Wang_ 評論0 收藏0
  • Express 實戰(zhàn)(五):路由

    摘要:的官方描述是是一個獨(dú)立于中間件和路由的實例,你可以將看作是只能執(zhí)行執(zhí)行中間件和路由的小心應(yīng)用。最大的不同在于只能已模塊形式存在并不能獨(dú)立運(yùn)行。另外,加密的公鑰也被稱為證書。客戶端在拿到公鑰證書后會向這樣的證書頒發(fā)機(jī)構(gòu)進(jìn)行驗證。 showImg(https://segmentfault.com/img/remote/1460000010820582); 作為 Express 中的最大特點(diǎn)...

    DevWiki 評論0 收藏0
  • Express 實戰(zhàn)(六):構(gòu)建 API 接口

    摘要:狀態(tài)碼的正確使用。解析請求獲取隨機(jī)數(shù)范圍并將生產(chǎn)的結(jié)果以格式返回。在代碼的最后,我們會在合法的參數(shù)返回內(nèi)生成隨機(jī)數(shù)并將結(jié)果返回給客戶端。雖然示例很簡單,但是它已經(jīng)包含了使用構(gòu)建的基本流程解析請求,設(shè)置狀態(tài)碼,返回響應(yīng)數(shù)據(jù)。 showImg(https://segmentfault.com/img/remote/1460000010820713); 在介紹了那么多 Express 核心概...

    AnthonyHan 評論0 收藏0
  • 3 Express-NodeJs+Express+Mysql實戰(zhàn)

    摘要:新手入門基于平臺的應(yīng)用開發(fā)框架本章節(jié)主要參考教程已經(jīng)寫的很棒了,直接將其教程地址為大家轉(zhuǎn)載過來初始化一個項目路由模板引擎淺析中間件與中間件與錯誤處理錯誤處理 Express新手入門: 基于 Node.js 平臺的 web 應(yīng)用開發(fā)框架http://www.expressjs.com.cn/ 本章節(jié)主要參考 nswbmw教程已經(jīng)寫的很棒了,直接將其教程地址為大家轉(zhuǎn)載過來 - [初始化一...

    mushang 評論0 收藏0
  • Express 實戰(zhàn)(三):Express 基礎(chǔ)

    摘要:同樣的的框架中也有被稱為中間件概念。所以,整個工作流有兩種可能情形另外,這些中間件函數(shù)中部分函數(shù)需要對響應(yīng)做出響應(yīng)。擴(kuò)展和在原來基礎(chǔ)上對和對象進(jìn)行了功能擴(kuò)展。除了對響應(yīng)對象進(jìn)行了拓展之 Express 框架的初衷是為了拓展 Node 內(nèi)置模塊的功能提高開發(fā)效率。當(dāng)你深入研究后就會發(fā)現(xiàn),Express 其實是在 Node 內(nèi)置的 HTTP 模塊上構(gòu)建了一層抽象。理論上所有 Express...

    KoreyLee 評論0 收藏0

發(fā)表評論

0條評論

mochixuan

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<