Express blog從零開始搭建(2)

package.json


{
    ...
    "scripts": {
        "start": "node ./bin/www   // 指定執行腳本
    },
    "dependencies": {
        "body-parser": ...,     
        "cookie-parser": ...,   
        "debug": ...,           
        "ejs": ...,             
        "express": ...,         
        "morgan": ...,          
        "serve-favicon": ...    
    }
}

body-parser: express中間件,作用是對post請求的請求體進行解析。
cookie-parser: express中間件,解析cookies
debug: 仿照nodejs核心調試技術的一個小型JavaScript調試工具。
ejs: 視圖模板渲染引擎
express: nodejs web應用程序框架
morgan: nodejs http請求日誌中間件
serve-favicon: nodejs 提供favicon的中間件

app.js


  • 設置視圖模板渲染引擎
// 指定渲染引擎目錄
app.set('views', path.join(__dirname, 'views');
// 指定渲染引擎
app.set('view engine', 'ejs');

回溯: Express blog從零開始搭建(1)中使用express -v -e .命令時,app.js中指定渲染引擎的代碼爲app.set('view engine', '-e'),並不是我們期望的ejs視圖模板引擎,因爲當使用-v參數時不能使用ejs的簡寫指令-e

  • 使用morgan打印日誌
app.use(logger('dev'));

實現終端打印日誌, dev參數,可以看morgan的源碼:

/**
 * dev (colored)
 */

morgan.format('dev', function developmentFormatLine (tokens, req, res) {
  // get the status code if response written
  var status = headersSent(res)
    ? res.statusCode
    : undefined

  // get status color
  var color = status >= 500 ? 31 // red
    : status >= 400 ? 33 // yellow
    : status >= 300 ? 36 // cyan
    : status >= 200 ? 32 // green
    : 0 // no color

  // get colored function
  var fn = developmentFormatLine[color]

  if (!fn) {
    // compile
    fn = developmentFormatLine[color] = compile('\x1b[0m:method :url \x1b[' +
      color + 'm:status \x1b[0m:response-time ms - :res[content-length]\x1b[0m')
  }

  return fn(tokens, req, res)
})

這裏註釋說明是以高亮的形式在終端輸出日誌。這裏如果不確定dev參數的用處,可以去掉,即直接使用app.use(logger()),然後就會在控制檯獲得如下報錯信息:

morgan deprecated undefined format: specify a format app.js:19:9
morgan deprecated default format: use combined format app.js:19:9

使用express-generate生成的express項目中使用到的中間件或依賴包,還是需要做了解的。在不關心細節的情況下,可以根據上面拋出的異常去查看源碼支持的幾種format。

function morgan(format, options) {
    ...
    return function logger(req, res, next) {
        ....
    }
}

morgan最後返回的是一個命名爲logger的中間件。

  • 使用body-parser解析post請求的請求體
app.use(bodyParser.json());     // 加載解析json的中間件
app.use(bodyParser.urlencoded({ extended: false })); // 加載解析urlencoded的中間件

bodyParser.json()解析json格式的數據
bodyParser.urlencoded()解析form表單提交的數據,也就是請求頭中包含: Content-Type: application/x-www-form-urlencoded

回顧一下Content-Type的四種類型:
1. application/x-www-form-urlencoded 常見的表單提交
2. multipart/form-data 文件提交
3. application/json json格式數據
4. text/xml xml格式數據

  • 加載解析cookie的中間件
app.use(cookieParser());
  • 設置public爲靜態資源存放的目錄
app.use(express.static(path.join(__dirname, 'public')));
  • 路由
app.use('/', index);
app.use('/users', users);
  • 捕獲404並轉發到錯誤處理程序
app.use(function(req, res, next)) {
    var err = new Error('Not Found');
    error.status = 404;
    // 轉發到錯誤處理程序
    next(err);
}

因爲沒有做特殊的處理,JavaScript的Error對象的所有公共屬性都會暴露。

  • 錯誤處理程序
app.use(function(err, req, res, next) {
    // 設置locals,只在開發環境中呈現錯誤信息
    res.locals.message = err.message;    // 設置locals錯誤信息
    res.locals.error = req.app.get('env') === 'development' ? err : {};     // 設置locals的error對象爲error
    // 渲染錯誤頁面
    res.status(err.status || 500);    // 填充res的狀態碼,程序未處理的返回500
    res.render('error');      // express/lib/response.js [res.render = function render(view, options, callback)]
});

與error頁面呈現的信息對應一下:

<h1><%= message %></h1>         // 錯誤信息
<h2><%= error.status %></h2>    // 錯誤狀態碼
<pre><%= error.stack %></pre>   // 錯誤堆棧信息,這裏是具體的錯誤信息

res.render('error');點明將會渲染的視圖,如果即將渲染的視圖路由不存在時,則會調用內置的錯誤處理程序。
如果訪問一個不存在的路由,內置的錯誤處理程序不會將路由重定向到某個路由,而是直接將錯誤信息打印在當前並不存在的路由頁面中,使得看上去是當前訪問的路由呈現的錯誤信息。
訪問不存在的路由

module.exports = app; // 將app.js作爲模塊導出
  • 內置錯誤處理程序

Express內置的錯誤處理程序,負責處理應用程序中可能遇到的任何錯誤,這個內置的錯誤處理中間件函數添加在中間件函數集的末尾。

原因:
如果將錯誤傳遞給next()且未在自定義的錯誤處理程序中處理,則將由內置的錯誤處理程序處理;錯誤將寫入客戶端的堆棧跟蹤內。堆棧跟蹤並不存在於生產環境。
在生產環境中,如果在響應之後調用next()時出錯,express的內置錯誤處理程序會關閉連接並使請求失敗。因此在自定義錯誤處理程序時,如果響應頭已經發送到客戶端,要考慮委託給內置錯誤處理程序處理。

bin/www


  • 文件聲明
#!/usr/bin/env node

聲明是node可執行文件

  • 設置端口
var port = normalizePort(process.env.PORT || 3000);
app.set('port', port);

如果設置了process.env.PORT,對其序列化並使用;否則使用默認端口3000。

  • 創建HTTP服務器
var server = http.createServer(app);
  • 監聽端口
server.listen(port);
  • HTTP server的error和listening事件監聽
server.on('error', onError);
server.on('listening', onListening);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章