【Express系列】第2篇——主程序的改造

上一篇對項目的目錄結構和 app.js 等一些文件作了一些改造,然而那只是開始。node

接下來將作進一步的改造和完善。web

咱們先看看幾個主要的腳本文件,下面的代碼是我稍微修改過並添加註釋的,方便理解每句代碼的意思。express

 

app.js:npm

var express = require('express'),
    path = require('path'),
    favicon = require('serve-favicon'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    routes = require('./routes/index'),
    users = require('./routes/users');

//生成一個 express 實例
var app = express();

//指定 web 應用的標題欄小圖標的路徑爲:/static/favicon.ico
app.use(favicon(path.join(__dirname, 'static', 'favicon.ico')));
//加載日誌中間件
app.use(logger('dev'));
//加載解析 json 的中間件
app.use(bodyParser.json());
//加載解析 urlencoded 請求體的中間件
app.use(bodyParser.urlencoded({ extended: false }));
//加載解析 cookie 的中間件
app.use(cookieParser());
//設置 static 文件夾爲存放靜態文件的目錄
app.use(express.static(path.join(__dirname, 'static')));

//路由控制器
app.use('/', routes);
app.use('/users', users);

//捕獲404錯誤,並轉發到錯誤處理器
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});


//錯誤處理器
if (app.get('env') === 'development') {
    //開發環境下的錯誤處理器,返回完整的錯誤對象
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.send({
code: 0, message: err.message, error: err }); }); } app.use(
function(err, req, res, next) { //生產環境下的錯誤處理器,只返回錯誤提示消息 res.status(err.status || 500); res.send({
code: 0, message: err.message }); });
//導出app實例供其餘模塊調用 module.exports = app;

本來錯誤處理器裏面調用了 res.render 方法,但它依賴於模版引擎。而前面我已經把模版引擎配置相關的代碼移除掉了,因此這裏改用 res.send 方法實現。json

代碼裏指定了網站小圖標,記得將小圖標文件放到對應位置,不然會報錯,實在沒有小圖片就把那行代碼註釋掉吧。api

 

start.js(PS:也就是以前的 bin/www)瀏覽器

#!/usr/bin/env node
//上面一行代表此文件是 node 可執行文件


var app = require('./app'),//引入 app.js 導出的 app 實例
    debug = require('debug')('test:server'),//引入 debug 模塊,打印調試日誌
    http = require('http'),//引入 http 模塊,用以建立 http 服務
    port = process.env.PORT || '3000',//環境變量若是設置了端口號,就用環境變量設置的,不然使用默認值3000
    server = null;

//設置端口號
app.set('port', port);

//建立 http 服務
server = http.createServer(app);

//監聽端上面設置的口號
server.listen(port);
//綁定錯誤事件處理函數
server.on('error', onError);
//綁定監聽事件處理函數
server.on('listening', onListening);

//錯誤事件處理函數
function onError(error) {
    if (error.syscall !== 'listen') {
        throw error;
    }

    var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;

    //對特定的錯誤類型作友好的處理
    switch (error.code) {
        case 'EACCES':
            console.error(bind + ' requires elevated privileges');
            process.exit(1);
            break;
        case 'EADDRINUSE':
            console.error(bind + ' is already in use');
            process.exit(1);
            break;
        default:
            throw error;
    }
}

//監聽事件處理函數
function onListening() {
    var addr = server.address(),
        bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
    
    debug('Listening on ' + bind);
}

原本在 start.js 裏有一個 normalizePort 函數,用來作端口號轉換的。但我以爲沒有什麼必要,只要在配置端口號的時候注意便可,不必作這樣的適配,因而將它移除,簡化代碼。cookie

 

routes/index.js 和 routes/users.js:app

//routes/index.js
var
express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('hello world'); }); module.exports = router;
//routes/users.js
var
express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.send('respond with a resource'); }); module.exports = router;

上面兩段腳本,生成一個路由實例用來捕獲訪問主頁的 GET 請求,導出這個路由並在 app.js 中經過 app.use 方法被加載。當訪問對應的 url 時,就會執行相應的處理回調。函數

index.js 原本也是使用了 res.render 方法,也被改爲使用 res.send 方法了。緣由和上面提到過的一樣修改點相同,這裏就再也不重複。

 

然而,目前的這種路由配置形式我以爲不怎麼美麗,由於咱們能夠試想一下,假如應用比較複雜,路由配置也相應的更復雜,那麼在 app.js 就會出現大量的路由配置相關代碼...

爲了不這種狀況,致使 app.js 過於臃腫,因而我將路由配置相關的代碼從新規劃了一下。

先看看改造後的目錄結構(主要變更在 routes):

原本 routes 目錄下有多個 js 文件,如今變成只有一個 main.js 了,而後裏面又多了一個 modules 目錄,裏面會有多個 js 文件,它們都是 main.js 的子模塊。

好比圖中看到的兩個 menuMod.js 和 userMod.js,分別是菜單以及用戶相關的業務邏輯,以此規則劃分模塊。

而 routes 目錄下的 main.js 則是一個路由配置的主文件,經過引入 routes/modules 目錄中的各個子模塊,指定不一樣請求方式,不一樣 url 所對應的不一樣回調函數。

改造過程當中,根據目錄結構的改變,還要對 app.js 做對應修改,下面看看修改後的代碼...

 

app.js

var express = require('express'),
    path = require('path'),
    favicon = require('serve-favicon'),
    logger = require('morgan'),
    cookieParser = require('cookie-parser'),
    bodyParser = require('body-parser'),
    routes = require('./routes/main');

//生成一個 express 實例
var app = express();

//指定 web 應用的標題欄小圖標的路徑爲:/static/favicon.ico
app.use(favicon(path.join(__dirname, 'static', 'favicon.ico')));
//加載日誌中間件
app.use(logger('dev'));
//加載解析 json 的中間件
app.use(bodyParser.json());
//加載解析 urlencoded 請求體的中間件
app.use(bodyParser.urlencoded({ extended: false }));
//加載解析 cookie 的中間件
app.use(cookieParser());
//設置 static 文件夾爲存放靜態文件的目錄
app.use(express.static(path.join(__dirname, 'static')));

//配置路由
routes(app);

//捕獲404錯誤,並轉發到錯誤處理器
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});


//錯誤處理器
if (app.get('env') === 'development') {
    //開發環境下的錯誤處理器,將錯誤信息渲染 error 模版並顯示到瀏覽器中
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

app.use(function(err, req, res, next) {
    //生產環境下的錯誤處理器,不會將錯誤信息泄露給用戶
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

//導出 app 實例供其餘模塊調用
module.exports = app;

 

routes/main.js

var _ = require('underscore'),//引入 underscore 模塊
    userMod = require('./modules/userMod'),//引入 user 模塊
    menuMod = require('./modules/menuMod'),//引入 menu 模塊
    config = {
        get: {
            '/user/list': userMod.getUserList,
            '/user/status': userMod.checkStatus,
            '/menu/list': menuMod.getMenuList
        },
        post: {
            '/user/save': userMod.save,
            '/menu/save': menuMod.save
        }
    };//路由配置



module.exports = function (app) {

    //分析路由配置對象,逐一處理
    _.each(config, function (subConfig, method) {

        _.each(subConfig, function (func, url) {

            app[method](url, func);

        });

    });

};

 

routes/modules/userMod.js

var userMod = {
    getUserList: function (req, res) {
        res.send('userList');
    },
    checkStatus: function (req, res) {
        res.send('userStatus');
    },
    save: function (req, res) {
        res.send('userSave');
    }
};

module.exports = userMod;

 

routes/modules/menuMod.js

var menuMod = {
    getMenuList: function (req, res) {
        res.send('menuList');
    },
    save: function (req, res) {
        res.send('menuSave');
    }
};

module.exports = menuMod;

 

上面的改造,還添加了一個項目依賴庫,它就是知名的 underscore。

打開命令行工具,進入項目根目錄,執行下面指令,安裝 underscore,並將它加入到 package.json 的 dependencies 之中:

npm install underscore --save

 

通過這樣的改造後,之後修改路由規則,只須要在 routes/main.js 裏修改 config 配置對象,而後根據須要添加新的子模塊,或者在原有的子模塊里加入新的方法便可。

須要注意的是,每一個子模塊裏的每個方法,都有兩個同樣的參數 req 和 res,也就是 request 對象 和 response 對象。

至於這兩個對象都提供了什麼方法,什麼屬性,能夠去查 API,傳送門:http://expressjs.jser.us/api

 

OK,到目前爲止,整個項目從建立到如今,已經面目全非了。

但我竟然跑都沒跑過一次...是否是感受很不靠譜?

其實這個項目是根據以前的經驗從新整理的,因此在弄的時候,基本就沒測試是否能運行了。

但爲防百密一疏,或者說是謹慎起見,這裏咱們運行一下試試看。

打開命令行工具,進入到項目根目錄,而後運行下面指令:

node start

若是沒看到報錯信息,說明服務成功運行了。

 

接下來,咱們用瀏覽器訪問 http://localhost:3000/user/list

若是能看到瀏覽區裏出現 userList,說明成功返回了

 

再試試用瀏覽器訪問 http://localhost:3000/user/status

瀏覽區裏出現 userStatus,OK,沒問題了

 

至此,咱們的應用已經成功實現了根據不一樣的請求,作出相應相應的基本功能。後面能夠根據須要慢慢完善細節了~

相關文章
相關標籤/搜索