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

資訊專欄INFORMATION COLUMN

Express 實戰(四):中間件

mochixuan / 1683人閱讀

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

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

本文將會詳細介紹 Express 的使用,其中主要內容包括:

中間件是什么?

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

中間件的使用。

如何實現自己的中間件。

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

希望在讀完本文后,你能對這個 Express 最主要的構成有更加清晰的認知。

中間件和中間件棧

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

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

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

在整個中間件棧的處理流中,最少有一個函數需要調用 res.end 方法結束響應處理。下面我們就通過搭建靜態文件服務來加深對中間件棧的理解。

示例:一個靜態文件服務器

創建一個文件夾并為此提供靜態文件服務。你可以在文件夾中存放任何文件,例如:HTML 文件、圖片。最終所有的這些文件都能通過示例程序進行網絡訪問。

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

日志記錄中間件。該函數會在終端打印所有的網絡請求,并在打印介紹后繼續下一個中間件函數。

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

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

流程圖如下:

明確示例的目標和需求后,下面我們就進行代碼實現。

準備工作

與之前一樣,新建工程目錄并將下面內容復制到 package.json 中:

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

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

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

第一個中間件:日志記錄

按照前面制訂的處理流程,首先需要實現的就是日志中間件。復制下面代碼到入口文件 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 函數,我們成功實現了應用中的第一個功能,即記錄每次網絡請求。當然這里還有一個問題,當前應用并不會對請求做出響應。這意味這:如果你用 npm start 拉起服務并訪問 loaclhost:3000 瀏覽器會一直掛起等待響應直到出現超時錯誤。不過不要擔心,等補全后面功能后我們就可以在該中間件調用 next() 將響應的任務交給后續中間件。

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

所有處理結束,發送 red.end 或者 Express 中的 red.sendFile 等函數結束響應。

調用 next 函數執行下一個中間件函數。

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

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

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

靜態文件服務中間件

靜態文件服務中間件應該有以下幾個功能:

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

如果文件存在則調用 res.sendFile 結束響應處理。

如果文件不存在則繼續調用下一個中間件從代碼角度來說就是調用 next

其中我們需要使用內置的 path 模塊指定路徑,然后使用內置的 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" 了。

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

404 處理中間件

404 中間件的任務就是發送 404 錯誤信息,復制下面的實現代碼并添加到靜態服務中間件后面:


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

// ...

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

到這里,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");
});

當然,這只是初步的代碼,還有很多地方可以進行優化。

將日志中間件替換為:Morgan

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

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

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

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

...

當你再次啟動服務并訪問資源時,終端將會打印包括 IP 地址在內的有用信息:

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

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

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

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

使用 Express 內置靜態文件中間件

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

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

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

完成替換后你會發現代碼相較之前明顯變的簡練了,與此同時功能反而比之前更強。另外,這些久經考驗的中間件模塊遠比自己的代碼實現功能更多也更可靠。此時 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");
});
錯誤處理中間件

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

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

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

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

假設,現在有四個中間件依次排開,其中第三個為錯誤處理中間件而其他的都是常規中間件。如果沒有出現錯誤的話,流程應該是:

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

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

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

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

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");
});

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

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

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

當然,除了打印錯誤信息之外,我們還可以通過觸發異常進入錯誤處理中間件函數,而該部分代碼實現如下:

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

異常觸發后接下來就是錯誤處理中間件的實現了。

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

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

// ...

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

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

// ...

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

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

總結

在本文中我們仔細探討了 Express 的核心模塊:中間件。其中的內容包括:

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

如何編寫自定義的中間件函數。

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

常見中間件模塊的使用。

原文地址

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84854.html

相關文章

  • 實戰】用 express+MongoDB 搭建一個完整的前端項目

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

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

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

    DevWiki 評論0 收藏0
  • Express 實戰(六):構建 API 接口

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

    AnthonyHan 評論0 收藏0
  • 3 Express-NodeJs+Express+Mysql實戰

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

    mushang 評論0 收藏0
  • Express 實戰(三):Express 基礎

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

    KoreyLee 評論0 收藏0

發表評論

0條評論

mochixuan

|高級講師

TA的文章

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