理解Express中介軟體

龍恩0707發表於2018-11-04

閱讀目錄

一:body-parser中介軟體

二:cookie-parser中介軟體

三:express-session 中介軟體

四:理解使用morgan記錄操作日誌

一:body-parser中介軟體

body-parser是一個HTTP請求體解析的中介軟體,該中介軟體的作用是解析客戶端請求的body中的內容的,使用該模組可以解析JSON、Raw、文字、URL-encoded格式的請求體。

如何使用?

在專案的根目錄下,執行命令,先下載 body-parser, 程式碼執行如下:

npm install body-parser --save

基本使用如下:

1. 首先需要引入進來: const bodyParser = require('body-parser');
2. 然後再進行解析,目前支援如下四種格式的解析,如下:

    1. 解析json資料:bodyParser.json(options)
    2. 解析二進位制格式(比如Buffer資料流這樣的):bodyParser.raw(options);
    3. 解析文字資料: bodyParser.text(options);
    4. 解析UTF-8的編碼資料:bodyParser.urlencoded(options);

    bodyParser.json(options); 它返回一個僅解析json格式資料的中介軟體。該方法支援任意Unicode編碼的請求體,且支援gzip和deflate編碼的資料壓縮。

options引數有如下選項,及各選項的含義分別如下解析:

1)inflate: 該引數預設為true,deflate壓縮資料被壓縮,設定為false,壓縮資料會被拒絕。
2)limit: 設定請求的最大資料量,預設為 '100kb'。
3)reviver: 傳遞給JSON.parse()方法的第二個引數。
4)strict: 預設為true,僅會解析Array和Object兩種格式,如果設定為false的話,就會解析所有的JSON.parse支援的格式。
5)type: 該選項用於設定為指定MIME型別的資料使用當前解析的中介軟體。該選項可以是一個函式或一個字串,如果是字串的話會
使用type-is來查詢MIME型別;如果是函式的話,中介軟體會通過fn(req)來獲取實際值,預設為 application/json.

bodyParser.raw(options); 它是返回一個將所有資料為Buffer格式處理的中介軟體。其後所有的req.body中會是一個Buffer值。

options引數有如下選項,及各選項的含義分別如下解析:

1)inflate: 該引數預設為true,deflate壓縮資料被壓縮,設定為false,壓縮資料會被拒絕。
2)limit: 設定請求的最大資料量,預設為 '100kb'。
3)type: 該選項用於設定為指定MIME型別的資料使用當前解析的中介軟體。該選項可以是一個函式或一個字串,如果是字串的話會
使用type-is來查詢MIME型別;如果是函式的話,中介軟體會通過fn(req)來獲取實際值,預設為 application/octet-stream.

bodyParser.text(options); 它是返回一個僅處理字串格式處理的中介軟體。該req.body所有的將會是一個字串值。

options引數有如下選項,及各選項的含義分別如下解析:

1)defaultCharset: 如果Content-Type沒有指定編碼的話,預設為 'utf-8'.
2)inflate: 該引數預設為true,deflate壓縮資料被壓縮,設定為false,壓縮資料會被拒絕。
3)limit: 設定請求的最大資料量,預設為 '100kb'。
4)type: 該選項用於設定為指定MIME型別的資料使用當前解析的中介軟體。該選項可以是一個函式或一個字串,如果是字串的話會
使用type-is來查詢MIME型別;如果是函式的話,中介軟體會通過fn(req)來獲取實際值,預設為 application/octet-stream.

bodyParser.urlencoded(options) 解析UTF-8編碼的資料,返回一個處理urlencoded資料的中介軟體。
options引數有如下選項,及各選項的含義分別如下解析:

1)extended - 當設定為false時,會使用querystring庫解析URL編碼的資料;當設定為true時,會使用qs庫解析URL編碼的資料。後沒有指定編碼時,使用此編碼。預設為true。
2)inflate - 設定為true時,deflate壓縮資料會被解壓縮;設定為true時,deflate壓縮資料會被拒絕。預設為true。
3)limit - 設定請求的最大資料量。預設為'100kb'
4)parameterLimit - 用於設定URL編碼值的最大資料。預設為1000
5)type - 該選項用於設定為指定MIME型別的資料使用當前解析中介軟體。這個選項可以是一個函式或是字串,當是字串是會使用type-is來查詢MIMI型別;當為函式是,中介軟體會通過fn(req)來獲取實際值。預設為application/octet-stream。

如上bodyParser有四種方法來解析不同的資料,但是在實際專案中,我們是如何使用的呢?
Express框架預設是使用body-parser作為請求體解析中介軟體的。我們可以在專案中的根目錄下建立一個bodyParser.js程式碼如下:
因此在執行執行,我們還需要安裝express框架;安裝命令如下:

npm install --save express

bodyParser.js 程式碼如下:

const express = require('express');

const bodyParser = require('body-parser');

const app = express();

// 對不同的路由使用不同的內容型別來解析

// 建立 application/json 解析
const jsonParser = bodyParser.json();

// 建立 application/x-www-form-urlencoded解析
const urlencodedParser = bodyParser.urlencoded({ extended: false });

// post /a 獲取URL編碼的請求體
app.post('/a', urlencodedParser, function(req, res) {
  if (!req.body) {
    return res.sendStatus(400);
  }
  console.log(req.body);
  res.send('welcome,' + req.body.userName);
  res.end();
});

// post /b 獲取URL編碼的請求體
app.post('/b', jsonParser, function(req, res) {
  if (!req.body) {
    return res.sendStatus(400);
  }
  res.send('welcome,' + req.body.userName);
  res.end();
});

const port = process.env.port || 3000;

app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

如上程式碼,當我們使用post請求 http://127.0.0.1:3000/a 請求的時候,他們使用的是 application/x-www-form-urlencoded格式來解析的,我這邊使用postman來演示下,如下所示:

當我們使用 post請求 http://127.0.0.01:3000/b 的時候,我們使用的是 application/json格式來解析的,那麼 Postman 以raw 方式傳送JSON 資料,如下圖所示:

注意:如果在模擬器上以非JSON格式傳送,則會獲得一個空的JSON物件。

注意:如果我們 app.use(bodyParser.urlencoded({ extended: false })) 這樣使用的話,那麼它會全域性使用所有的都是以 application/x-www-form-urlencoded 來進行解析的。如果單個路由使用了 格式來解析的話,那麼使用的格式的優先順序更高。

二:cookie-parser中介軟體

cookieParser中介軟體是用於獲取web瀏覽器傳送的cookie中的內容。因為http協議它是無狀態的,使用者從A頁面跳轉到B頁面會發起http請求,當伺服器端返回響應之後,當使用者A繼續訪問其他頁面的時候,伺服器端無法獲知該狀態的,因此需要一個cookie來記錄使用者的狀態。

至於http無狀態的原因,當初為什麼需要這麼設計,官方文件是如下說明的:

1. HTTP最初的目的是為了提供一種釋出和接收HTML頁面的方法,那個時候只有純粹靜態HTML頁面,因此不需要協議來保持狀態。

2. 使用者在收到響應的時候,一般會花一些時間來閱讀頁面,如果客戶端和伺服器端還保持連線的話,那麼這個連線在大多數時候都將是空閒的,這是一種資源的浪費,因此HTTP最初的設計是短連線,即:客戶端和服務端完成一次請求和響應之後就斷開TCP連線,因此服務端無法知道客戶端的下一個動作。

3. 這樣設計也可以使HTTP協議更加的相對簡單,那麼這種簡單可以賦予HTTP更強的擴充套件能力,因此比如session就是一種協議擴充套件。或者後來出現了CGI動態技術等。

而正是因為HTTP不能保持狀態的原因,因此客戶端會出現cookie來儲存狀態,及後面即將講解的session。因此瀏覽器第一次向伺服器傳送請求,伺服器會返回一個cookie給客戶端瀏覽器,瀏覽器下一次發請求的時候,會把該cookie傳遞過去,因此伺服器端就知道該cookie對應值,就能獲取該狀態的。

1) cookie是如何建立的?

express直接提供了對應的API,只要在響應中設定cookie即可,如下程式碼所示:

function(req, res, next) {
  res.cookie(name, value, [, options]);
}

如上程式碼,express會將其填入 Response Header中的Set-Cookie中,達到瀏覽器中設定cookie的作用的。

如上引數 options 型別為一個物件,可以使用的屬性有如下:

domain: cookie在什麼域名下有效,型別為String, 預設為網站的域名。
expires: cookie的過期時間,型別為Date, 如果沒有設定或設定為0,那麼該cookie只在這個會話中有校,關閉瀏覽器的話,那麼cookie就會被瀏覽器刪除。
httpOnly: 只能被web server訪問。型別為布林型(Boolean)
maxAge: 實現expires的功能,設定cookie的過期時間,型別為String,指明從現在開始,過多少毫秒以後,cookie到期。
path: cookie在什麼路徑下有校,預設為 '/'
secure:  只能被HTTPS使用,預設為false。
signed: 使用簽名,預設為false。

一般簡單的用法如下:

res.cookie('name', 'kongzhi', { domain: 'xxx.abc.com', path: '/', secure: true });

//cookie的有效期為900000ms
res.cookie('age', '30', { expires: new Date(Date.now() + 900000), httpOnly: true });

//cookie的有效期為900000ms
res.cookie('age', '30', { maxAge: 900000, httpOnly: true });

//cookie的value為物件
res.cookie('kongzhi1', { items: [1,2,3] });

res.cookie('kongzhi2', { items: [1,2,3] }, { maxAge: 900000 });

res.cookie('name', 'longen', { signed: true });

2)cookie是如何被刪除的?
express也提供了api刪除瀏覽器中的cookie,只需要呼叫對應的API即可,如下程式碼:

function(req, res, next) {
  res.clearCookie(name, [, options]);
}

3) cookie-parser 讀取cookie
在使用 cookie-parser 外掛之前,我們首先要安裝該外掛,執行命令如下:

npm install --save cookie-parser

使用的demo基本如下(在專案的根目錄下新建 cookieParser.js, 程式碼如下):

//引入cookieparser 框架,讀取客戶端傳送的cookie
const express = require('express');

const cookieParase = require('cookie-parser');

var app = express();

//如果沒有,下面的req.cookies 會返回undefined
app.use(cookieParase());

app.use('/', function (req,res) {
    res.cookie('user', 'kongzhi');
    console.log(req.cookies);
    res.send('cookie我來了');
});
const port = process.env.port || 3001;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

使用瀏覽器訪問 http://127.0.0.1:3001/ 然後再看下控制檯,可以看到如下讀取到了:

下面我們再來看個demo來理解使用cookie,首先需要有一個提交cookie的頁面 cookie.html, 該頁面顯示一個 '提交cookie' 按鈕,在使用者點選該按鈕後,客戶端會建立一些cookie,然後使用 XMLHttpRequest物件向伺服器端提交資料,該頁面所有的cookie也會被提交上去,待接到伺服器端響應資料後會將這些資料顯示在頁面中。

在專案的根目錄中新建 cookie.html 程式碼如下:

<!DOCTYPE html> 
<html>
<head>
  <title>cookie</title>
  <meta charset="utf-8">
  <style>
    #result {
      font-size: 16px;
    }
  </style>
</head>
<body>
  <div id="app">
    <form id="form1" method="post" action='index.html'>
      <input type="button" value="提交cookie" onclick="cookieTest()" />
    </form>
    <div id="result"></div>
  </div>
  <script type="text/javascript">
    function cookieTest() {
      var xhr = new XMLHttpRequest();
      xhr.open('post', 'cookie.html', true);
      
      document.cookie = 'username=kongzhi';
      document.cookie = 'age=30';
      xhr.onload = function() {
        if (this.status === 200) {
          document.getElementById('result').innerHTML = this.response;
        }
      };
      xhr.send();
    }
  </script>
</body>
</html>

在專案中的根目錄新建 testCookie.js 程式碼如下:

const express = require('express');

const cookieParase = require('cookie-parser');

const fs = require('fs');

const app = express();

app.use(cookieParase());

app.get('/cookie.html', function(req, res) {
  res.sendfile(__dirname + '/cookie.html');
});

app.post('/cookie.html', function(req, res) {
  for (const key in req.cookies) {
    res.write('cookie名:' +key);
    res.write(',cookie值為:'+req.cookies[key] + "<br/>");
  }
  res.end();
});

const port = process.env.port || 3002;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

然後我們進入專案中的根目錄執行 node testCookie.js ,接著瀏覽器訪問 http://127.0.0.1:3002/cookie.html 後點選提交,
可以看到如下顯示了。如下所示:

三:express-session 中介軟體

session中介軟體用於儲存使用者資料提供一個session管理器,它和cookie類似,都是記錄使用者狀態的,而與cookie不同的是:
cookie有如下限制:
1. cookie儲存的大小最多為4kb。
2. cookie有域或路徑的概念,並且最重要的一點是 cookie安全性很低,一般重要的資訊,比如使用者名稱和密碼這樣的不允許放入cookie裡面的。

如上cookie有最主要上面兩點的缺點,因此對於安全性的一些資訊建議放到session上儲存,session是儲存到伺服器上的。

session基本原理:當客戶端向服務端請求時,會建立一個session標識(比如叫jsessionid)存在客戶端的cookie當中,每次請求的時候伺服器端首先會檢查這個客戶端的請求裡面是否已經包含了一個session標識,如果已經包含了,那麼伺服器端就會根據該session標識把對應的資料檢索出來,如果不包含的話,服務端會建立一個新的session標識傳給客戶端cookie中,以後客戶端每次請求的時候,從cookie中獲取該標識把它傳遞過來。

服務端執行session機制的時候會生成Session口令,在Tomcat預設會採用jsessionid這個值,但是在其他伺服器上會有所不同,比如Connect預設會叫 connect_uid, 雖然把一些敏感資訊放到cookie當中是不可取的,但是將口令放在cookie中還是可以的,如果口令被篡改了的話,就丟失了對映關係,也無法修改服務端存在的資料了,並且session的有效期是非常短的,一般為20分鐘,如果在20分鐘內客戶端和伺服器端沒有產生互動,服務端會將資料刪除掉。

express-session 是伺服器端儲存資料的中介軟體,安裝命令如下所示:

npm install express-session --save

然後在js中引入後,再接著use即可,簡單的程式碼如下:

const express = require('express');
const app = express();
const session = require('express-session');
app.use(session(options));

如上引數 options 值為一個物件,它有如下屬性值:

key: String型別,用於指定用來儲存session的cookie名稱,預設為 connect.sid.
store: 屬性值為一個用來儲存session資料的第三方儲存物件。
fingerprint: 屬性值為一個自定義指紋生成的函式。
cookie: 屬性值為一個用來指定儲存session資料的cookie設定的物件,預設值為 {path: '/', httpOnly: true, maxAge: 14400000}, path是用於指定cookie儲存的路徑,httpOnly屬性值用於指定是否只針對http儲存的cookie,maxAge用於指定cookie的過期時間,單位為毫秒。

secret: 屬性值為一個字串,用來指定來對session資料進行加密的字串。

下面我們來和cookie一樣,做一個簡單的demo,來理解下使用 session的實列。

在頁面中index.html 有一個form表單post提交資料,頁面一訪問的時候,監聽get請求,給session設定資料,比如使用者名稱和密碼這樣的,然後當我按鈕提交post請求的時候,我們在伺服器端監聽post請求,然後把剛剛儲存的session資料列印出來,程式碼如下:

session.html 程式碼如下:

<!DOCTYPE html> 
<html>
<head>
  <title>cookie</title>
  <meta charset="utf-8">
  <style>
    #result {
      font-size: 16px;
    }
  </style>
</head>
<body>
  <div id="app">
    <form id="form1" method="post" action='session.html'>
      <input type="submit" />
    </form>
    <div id="result"></div>
  </div>
</body>
</html>

session.js 程式碼如下:

const express = require('express');
const fs = require('fs');
const app = express();
const cookieParase = require('cookie-parser');
const sessionParser = require('express-session');

app.use(cookieParase());
app.use(sessionParser({secret: 'test'}));

app.get('/session.html', function(req, res) {
  res.sendfile(__dirname+'/session.html');
  req.session.username = 'kongzhi111';
  req.session.password = '123456';
});

app.post('/session.html', function(req, res) {
  console.log(req.session.username);
  console.log(req.session.password);
  res.end();
});

const port = process.env.port || 3003;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

在命令列中,執行命令 node session.js, 然後在瀏覽器中 訪問 http://127.0.0.1:3003/session.html ,點選按鈕後,在命令列中列印如下資訊,如下所示:

1)理解使用 http.IncomingMessage 中regenerate 方法重新生成一個session管理器

我們也可以使用 http.IncomingMessage物件的session屬性值物件的 regenerate 方法重新生成一個session管理器,如下基本程式碼:

req.session.regenerate(function(err) {});

session.js 程式碼改成如下:

const express = require('express');
const fs = require('fs');
const app = express();
const cookieParase = require('cookie-parser');
const sessionParser = require('express-session');

app.use(cookieParase());
app.use(sessionParser({secret: 'test'}));

app.get('/session.html', function(req, res) {
  res.sendfile(__dirname+'/session.html');
  req.session.username = 'kongzhi111';
  req.session.password = '123456';
  req.session.regenerate(function(err) {
    if (err) {
      console.log('session重新初始化失敗');
    } else {
      console.log('session被重新初始化');
    }
  });

});

app.post('/session.html', function(req, res) {
  console.log(req.session.username);
  console.log(req.session.password);
  res.end();
});

const port = process.env.port || 3003;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

在頁面瀏覽器訪問 http://127.0.0.1:3003/session.html 後,命令列中會列印 如下所示:

然後點選提交後,命令中列印如下:

2)理解使用 http.IncomingMessage 中的destroy方法銷燬當前在用的session管理器

destroy使用方式如下:

req.session.destroy(function(err) {});

session.js 程式碼改成如下:

const express = require('express');
const fs = require('fs');
const app = express();
const cookieParase = require('cookie-parser');
const sessionParser = require('express-session');

app.use(cookieParase());
app.use(sessionParser({secret: 'test'}));

app.get('/session.html', function(req, res) {
  res.sendfile(__dirname+'/session.html');
  req.session.username = 'kongzhi111';
  req.session.password = '123456';
  req.session.destroy(function(err) {
    if (err) {
      console.log('session銷燬失敗');
    } else {
      console.log('session被銷燬');
    }
  });

});

app.post('/session.html', function(req, res) {
  console.log(req.session.username);
  console.log(req.session.password);
  res.end();
});

const port = process.env.port || 3003;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

同理在瀏覽器中執行 http://127.0.0.1:3003/session.html 後,在命令列中看到如下資訊:

然後當我們點選按鈕後,在命令列中看到如下資訊:

3)檢視http.IncomingMessage物件的session中儲存cookie屬性的所有資訊

session程式碼如下:

const express = require('express');
const fs = require('fs');
const app = express();
const cookieParase = require('cookie-parser');
const sessionParser = require('express-session');

app.use(cookieParase());
app.use(sessionParser({secret: 'test', cookie: {maxAge: 3600000}}));

app.get('/session.html', function(req, res) {
  res.sendfile(__dirname+'/session.html');
  console.log(req.session.cookie);
});

const port = process.env.port || 3003;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

檢視效果如下:

4)使用http.IncomingMessage物件session物件獲取cookie的剩餘時間

session.js 程式碼如下:

const express = require('express');
const fs = require('fs');
const app = express();
const cookieParase = require('cookie-parser');
const sessionParser = require('express-session');

app.use(cookieParase());
app.use(sessionParser({secret: 'test', cookie: {maxAge: 3600000}}));

app.get('/session.html', function(req, res) {
  res.sendfile(__dirname+'/session.html');
  var h = 3600000;
  req.session.cookie.expires = new Date(Date.now() + h);
  req.session.cookie.maxAge = h;
  setTimeout(function() {
    console.log('cookie的剩餘時間'+req.session.cookie.maxAge);
  }, 5000);
});

const port = process.env.port || 3003;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

列印如下所示:

我們最後再來看下瀏覽器請求頭資訊,如下:

請求頭看到 伺服器會返回 cookie 和 用於指定用來儲存session的cookie名稱,預設為 connect.sid, connect.sid 是session的簽名,作用是知道session是否被修改過。

5)使用Express + session 實現使用者登入

當我們登入淘寶網站的時候,登入成功後,我們沒有點選退出按鈕或有效期沒有過期的情況下,我們把瀏覽器關閉掉,重新進入淘寶的首頁會發現我們的session會話還一直保持登入狀態,那麼這個狀態就是session幫我們儲存下來的。那麼它在node中是如何實現的呢?

使用者登入的基本原理是:如果使用者沒有登入或者登入時使用者名稱或密碼錯誤的時候,那麼伺服器將該請求重定向到登入頁,或者的話就登入成功,登入成功後,伺服器需要記錄儲存該客戶端的登入狀態,那麼下一次伺服器請求主頁的時候,會先判斷客戶端的登入狀態,如果登入狀態在
有效期內是有效的,就直接進入主頁,否則的話,和之前一樣會重定向到登入頁面。

下面如何使用session來做這件事的呢?

下面我們需要如下目錄結構

|-- demo1           # 專案根目錄
| |--- login.html   # 登入頁面
| |--- home.html    # 主頁
| |--- app.js       # node服務端

至於 package.json 和專案是同目錄的。下面先看下 login.html 程式碼如下:

<!DOCTYPE html> 
<html>
<head>
  <title>expree+session實現使用者登入</title>
  <meta charset="utf-8">
</head>
<body>
  <div id="app">
    <form action="/login" method="post">
      <p>
        <label>使用者名稱:</label>
        <input type="text" name="username" />
      </p>
      <p>
        <label>密碼:</label>
        <input type="password" name="password" />
      </p>
      <input type="submit" value="提交" />
    </form>
  </div>
</body>
</html>

home.html 程式碼如下:

<!DOCTYPE html> 
<html>
<head>
  <title>expree+session實現使用者登入</title>
  <meta charset="utf-8">
</head>
<body>
  <div id="app">
    <div>
      使用者名稱:<span><%= username %></span>
      <a href="/logout">退出登入</a>
    </div>
  </div>
</body>
</html>

如上home.html 程式碼,使用了 <%= username %> 這樣的語法,這個是ejs模板語法,因此我們需要安裝一下 ejs 如下命令安裝:

npm install ejs --save

下面我們再來看看伺服器端 app.js 程式碼如何處理業務的,程式碼如下:

const express = require('express');
const app = express();

const session = require('express-session');
const bodyParser = require('body-parser');
const ejs = require('ejs').__express;

app.set('views', __dirname); // 設定模板目錄
app.set('view engine', 'html'); // 設定模板引擎為 html
app.engine('html', ejs); // 使用ejs模板引擎解析html檔案中ejs語法

app.use(bodyParser.json()); // 使用 body-parser中介軟體 json形式

// 解析UTF-8編碼的資料,返回一個處理urlencoded資料的中介軟體
app.use(bodyParser.urlencoded({ extended: true })); 

// 使用session中介軟體 設定cookie相關的資訊

app.use(session({
  secret: 'test', // 對 session id 相關的cookie 進行加密簽名
  cookie: {
    maxAge: 1000 * 60 * 1  // 設定 session的有效時間,單位為毫秒,設定有效期為1分鐘
  }
}));

// get 登入頁面
app.get('/login', (req, res) => {
  res.sendFile(__dirname + '/login.html');
});

// 監聽post表單事件 使用者登入
app.post('/login', (req, res) => {
  // 這裡沒有連線資料庫,所以使用者名稱和密碼假如是寫死的,所以簡單進行判斷一下
  if (req.body.username === 'kongzhi' && req.body.password === '123456') {
    req.session.username = req.body.username; // 登入成功,把登入資訊儲存到session裡面去了
    // 登入成功後,進行重定向到首頁
    res.redirect('/');
  } else {
    // 使用者名稱或密碼登入錯誤
    res.json({
      'code': 1,
      'errorMsg': '賬戶或密碼錯誤'
    });
  }
});

// 處理重定向首頁的邏輯程式碼
app.get('/', (req, res) => {
  // 如果session有已經登入的使用者名稱的話,且有效期有效的話,則到 home.html, 否則重定向到登入頁面
  if (req.session.username) {
    res.render('home', {username: req.session.username});
  } else {
    res.redirect('login');
  }
});

// 處理退出的邏輯
app.get('/logout', (req, res) => {
  req.session.username = null; // 刪除session
  // 重定向到登入頁面
  res.redirect('login');
});

const port = process.env.port || 3004;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

首先在命令列中 執行 node app.js ,然後在瀏覽器中訪問 http://127.0.0.1:3004 後,如下所示:

當我輸入正確的使用者名稱和密碼成功後,如下圖所示:

session的有效期是1分鐘,在1分鐘之內不管如何重新整理頁面,都是在home.html頁面,1分鐘之後,session過期了,再重新整理頁面會自動跳轉到登入頁面去了。這時候就需要重新登入了。當然在home頁面,你可以點選退出按鈕,退出到登入頁面去。

四:理解使用morgan記錄操作日誌

morgan中介軟體是來記錄詳細的操作日誌或錯誤日誌的。

1)morgan 安裝命令如下所示:

npm install --save morgan

2) 基本使用方式如下:

const express = require('express');
const app = express();
const morgan = require('morgan');
app.use(morgan(format, [, options]));

如上程式碼使用morgan, morgan(format, options);

引數 format(可選)該引數定義了幾種日誌格式,每種格式都有對應的名稱,預設是default,詳細有哪些格式,請看github官網(https://github.com/expressjs/morgan/#predefined-formats)

引數 options(可選),包含 stream, skip, immediate
stream: 日誌輸出流配置,預設是 process.stdout
skip: 是否跳過日誌記錄,使用方式看github上(https://github.com/expressjs/morgan/#skip)
immediate: 布林值,預設false,含義是在請求返回後,再記錄日誌,如果為true的話,含義是一收到請求,就記錄日誌。

1)下面我們來看下簡單的demo來理解下morgan的使用:

基本結構目錄如下:

---morgan           #專案的資料夾
 |--- morgan.html   # html檔案
 |--- morgan.js     # js檔案

morgan.html 程式碼如下:

<!DOCTYPE html> 
<html>
<head>
  <title>morgan</title>
  <meta charset="utf-8">
</head>
<body>
  <div id="app">
    <form id="form1">
      <input type="text" id="username" />
      <input type="button" value='提交' onclick="clickFunc()"/>
    </form>
    <div id="result"></div>
  </div>
  <script type="text/javascript">
    function clickFunc() {
      var obj = {
        username: document.getElementById('username').value
      };
      var xhr = new XMLHttpRequest();
      xhr.open('POST', 'morgan.html', true);
      xhr.onload = function(e) {
        if (this.status === 200) {
          document.getElementById('result').innerHTML = this.response;
        }
      };
      xhr.send(JSON.stringify(obj));
    }
  </script>
</body>
</html>

morgan.js 程式碼如下:

const express = require('express');
const morgan = require('morgan');
const app = express();

// 使用中介軟體,'combined' 是日誌顯示的格式,具體看github上(https://github.com/expressjs/morgan/#predefined-formats)
app.use(morgan('combined'));

app.get('/morgan.html', (req, res) => {
  res.sendFile(__dirname + '/morgan.html');
});

// post 請求監聽 
app.post('/morgan.html', (req, res) => {
  req.on('data', (data) => {
    console.log(data.toString());
    res.end();
  });
});

const port = process.env.port || 3005;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

當我們在專案的對應目錄下 執行 node morgan.js 會啟動伺服器,然後在瀏覽器中 http://127.0.0.1:3005/morgan.html中訪問的時候,會列印出如下資訊:

當我們輸入內容後,點選提交,會列印如下日誌資訊,如下所示:

2)如何將日誌資訊儲存到檔案內?
morgan提供了寫入檔案流將這些日誌相關的資訊儲存到檔案中,如下morgan.js程式碼改成如下:

const express = require('express');
const morgan = require('morgan');
const app = express();
const fs = require('fs');
const path = require('path');
const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'));


// 使用中介軟體,'combined' 是日誌顯示的格式,具體看github上(https://github.com/expressjs/morgan/#predefined-formats)
app.use(morgan('combined', {stream: accessLogStream}));

app.get('/morgan.html', (req, res) => {
  res.sendFile(__dirname + '/morgan.html');
});

// post 請求監聽 
app.post('/morgan.html', (req, res) => {
  req.on('data', (data) => {
    console.log(data.toString());
    res.end();
  });
});

const port = process.env.port || 3005;
app.listen(port, () => {
  console.log('http://127.0.0.1:%s', port)
});

如上程式碼,把日誌資訊寫入檔案中 在morgan.js 同級目錄下會自動生成 access.log 日誌檔案。

上面所有的demo,可以到github上檢視

相關文章