Nodejs之MEAN棧開發(一)---- 路由與控制器


因為工作需要,最近再次學習了node,上一次學習node是2014年,純粹是個人興趣,學了入門之后沒有運用,加上趕別的項目又不了了之。這次正好撿起來。廢話不多說,這里的MEAN指的是MongodbExpressAngularNode。 通過整個項目逐步整合在一起。MEAN棧最大的特色不是運用了哪些框架或第三方,而是前后端都是一種語言,即JavaScript。早些年我也是對node抱着疑態度,覺得這個頁面上操作dom的腳本語言,能扛得起后端那么多模塊嗎?但懷疑不防多了解一下,才決定寫這個系列的文章。

Mongodb做數據存儲,Express是基於node的后端框架,Angular是前端框架,Node是后端運行環境。安裝過程和node的特性就不講了,網上一大把。開發環境是VS2013.安裝了NTVS。node安裝完后,有時候需要設置下環境變量。在cmd目錄下輸入 node -v 如果顯示版本號,則說明安裝正確了。

起始工程

在VS中新建項目,選擇JavaScript-->Node.js,選擇Express4的應用。

  

  為了避免一直Ctrl+C,安裝nodemon,文件更新,它會自動重啟,-g表示安裝成全局。

npm install nodemon -g

修改routes文件夾下的index.js中的title為ReadingClub。然后用cmd切到工程目錄,輸入nodemon啟動工程。 

 在瀏覽器里面訪問lochost:3000 ,成功打開:

 先看routes文件夾下面的index.js ,這就是一個簡單的路由,處理的路徑為“/”,請求方式get,req代表的是request,res代表的response。

 

render方法有兩個參數,“index”,代表的是要渲染的視圖模板名稱,這里默認的視圖引擎是jade,而后面{title:'ReadingClub'}就是傳遞到視圖的數據模型。這里和Asp.net MVC 的return View() 有些相似,而這里的function就相當 Asp.net MVC中Controller的一個Action。View()默認是對應當前Action名稱的視圖。而render必須指定。

res 也可以直接發回一個響應

res.send('respond with a resource');

建立Controllers

 不像Asp.net MVC有默認的路由規則,Express的路由需要一個個配置,不妨把Controller提出來。但在此之前,我們先修改一下目錄。如下,建立app_server文件夾。里面分controllers、views和routes。可以把原來的views和routes直接移進去。

在controllers文件夾中新建一個home.js,加入三個方法:index、books和about。

module.exports.index = function(req, res) {
    res.render('index', { title: 'Index' });
};

module.exports.books = function(req, res) {
    res.render('books', { title: 'Books', });
};

module.exports.about = function (req, res) {
    res.render('about', { title: 'About' });
};

路由

同樣,在view文件夾中把index.jade復制兩遍,修改為books.jade和about.jade. 然后我們修改routes下的index.js,運用Express框架自帶的Router。

var express = require('express');
var router = express.Router();
var homeController = require('../controllers/home');

router.get('/', homeController.index); router.get('/about', homeController.about); router.get('/books', homeController.books);

module.exports = router;

這時候還是無法運行的,因為我們改變了目錄結構還沒有在app.js中重新設定。首先設置路由:

var routes = require('./app_server/routes/index');
app.use('/', routes);

修改視圖引擎的起始位置

//app.set('views', path.join(__dirname, 'views'));
app.set('views', path.join(__dirname, 'app_server', 'views'));

__dirname代表的是根目錄。然后再瀏覽器訪問/books或者/about 。

 這樣就分離了controller,請求通過路由抵達控制器,控制器將模型數據填充到對應的視圖的模板.這就是我們熟悉的MVC模式。我們再看router.METHOD方法定義。

router.METHOD(path, [callback, ...] callback)

這里的METHOD指get,post,put和delete等。因為我們可以定義:

router.get('/book/:bookId', homeController.detail);
router.put('/book/:bookId', homeController.updateBook);
router.delete('/book/:bookId', homeController.deleteBook);

雖然路徑都是一樣,但是代表的是不同的用意,完全restful,:bookId表示是參數。

同樣支持正則匹配,會匹配類似於這樣的‘GET /commits/71dbb9c’

router.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
  var from = req.params[0];
  var to = req.params[1] || 'HEAD';
  res.send('commit range ' + from + '..' + to);
});

如果每個請求都需要做某種處理,可以用all方法:

router.all('*', requireAuthentication, loadUser);

這等價於:

router.all('*', requireAuthentication)
router.all('*', loadUser);

Asp.net MVC的路由每一個都需要設置名稱,且不能重復出現,且匹配到之后就不再匹配,Express沒有這個限制,匹配到之后只要沒返回響應就會向下繼續傳遞。相對而言,Express的Router更靈活一些。

更多細節請參考官方API:http://www.expressjs.com.cn/4x/api.html#router

接下來我們回顧下整個app.js。

app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./app_server/routes/index');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'app_server', 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;
View Code

1.模塊定義:

首先看到很多的require的語法。

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./app_server/routes/index');

require表示應用一個模塊,npm上已經有超過25萬個包。這些能直接引用的模塊,是已經安裝在node_modules文件中。如果是自己定義的模塊,比如routes 就要用相對路徑。在node中模塊分以下幾類:

  • 核心模塊,如http,fs,path等
  • .或..開始的相對路徑文件模塊
  • 以/開始的絕對路徑文件模塊
  • 非路徑形式的文件模塊,如自定義的模塊。

核心模塊會優先加載,以相對或絕對路徑加載的模塊,require都會轉為真實路徑,將編譯執行后的結果放到緩存中,這樣二次加載就會更快。require能加載.js,.node.json的文件,其余擴展名都會被當.js文件載入。模塊與文件是一一對應的,一個文件夾的模塊就稱作包,包通常是一些模塊的集合。require是用來獲取模塊,而exports對象就是用來定義模塊。

module.exports.hello = function() {
console.log('Hello.');
};

相當於是定義接口,給外部調用。而上面的路由就是把一整個對象封裝到模塊中。

module.exports = router;

在app.js中直接獲取到整個路由對象:

var routes = require('./app_server/routes/index');

看到module.exports直接賦值router有點奇怪,會想不會覆蓋掉其他的模塊嗎,但事實上在編譯的過程中,node會對獲取的JavaScript文件內容進行包裝,等於是每個文件之間都進行了作用域的隔離。

2.app.set

app.js中用set方法設置了路由起始路徑和視圖引擎。 

app.set('views', path.join(__dirname, 'app_server', 'views'));//這里我們修改了路徑在app_server文件夾下
app.set('view engine', 'jade');//默認的視圖引擎是jade

還可以設置路由是否忽略大小寫,默認是不忽略。

app.set('case sensitive routing',true)

還可以設置環境變量是開發環境還是生產環境,更多的設置可以參考官方文檔:http://www.expressjs.com.cn/4x/api.html#app.settings.table

3.app.use

use方法是用來注冊一系列中間件,監聽端口上的請求,中間件利用了尾觸發機制,每個中間件傳遞請求對象,響應對象和尾觸發函數,通過隊列形成一個處理流。

 最簡單的中間件形式:

app.use(function (req, res, next) {
  console.log('Time: %d', Date.now());
  next();
})

看下各個中間件的作用:

app.use(logger('dev')); //日志,在開發環境下用彩色輸出響應狀態,會顯示請求方式,響應時間和大小。
app.use(bodyParser.json());//解析json請求。
app.use(bodyParser.urlencoded({ extended: false }));//解析form請求(含有key-value鍵值對),false表示value的類型是string或array,為true表示任意類型。
app.use(cookieParser());//解析cookie
app.use(require('stylus').middleware(path.join(__dirname, 'public')));//使用stylus做css預編譯,並指定路徑。
app.use(express.static(path.join(__dirname, 'public')));//靜態文件路徑

4.error

我們看到在設置了路由之后,如果請求還沒返回則認為頁面沒有找到,這個時候app拋出一個error。並繼續往下傳遞

app.use('/', routes);

// catch 404 and forward to error handler
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);//如果不是404就認為是內部錯誤
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// 生產環境錯誤處理
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

檢測到異常的時候,就渲染error模板。 接下來看下error模板,簡單介紹下jade語法:

extends layout  //相當於Asp.net MVC 設置Layout

block content //相當於 Asp.net MVC RenderBody
  h1= message  //顯示message
  h2= error.status  //顯示錯誤狀態
  pre #{error.stack} //錯誤堆棧

這樣就能處理404和500錯誤頁面。

小結:至此,整個默認工程已經介紹完,這一節通過Express框架建立一個基本的MVC工程,了解基本的請求和響應,node的基本模塊和中間件;並初步設置了路由,建立起專門的controller;解讀了app.js中的相關代碼;下一節關注模型和視圖。時至今日,node的開發環境已經很完善,從09年到現在這個技術已經走過了7年了,已經有很多書籍資料,國內的cnode社區很活躍。如果把技術比喻成股票的話,java,C#,PHP這些無疑是大盤白馬股,學這樣的技術風險小,不愁找不到工作。而node這樣的就像創業板股票,你也許認為這有很大的泡沫,認為新的公司不過是炒概念,但他就是在快速增長着。

 源碼:http://files.cnblogs.com/files/stoneniqiu/Reading.rar

參考文章:

http://www.tuicool.com/articles/emeuie 

http://www.2cto.com/kf/201207/142885.html

http://www.tuicool.com/articles/emeuie

https://github.com/expressjs/body-parser

書籍:《深入淺出nodejs》《Getting MEAN with Mongo, Express, Angular, and Node


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM