由於工做須要,最近再次學習了node,上一次學習node是2014年,純粹是我的興趣,學了入門以後沒有運用,加上趕別的項目又不了了之。此次正好撿起來。廢話很少說,這裏的MEAN指的是Mongodb、Express、Angular和Node。 經過整個項目逐步整合在一塊兒。MEAN棧最大的特點不是運用了哪些框架或第三方,而是先後端都是一種語言,即JavaScript。早些年我也是對node抱着疑態度,以爲這個頁面上操做dom的腳本語言,能扛得起後端那麼多模塊嗎?但懷疑不防多瞭解一下,才決定寫這個系列的文章。css
Mongodb作數據存儲,Express是基於node的後端框架,Angular是前端框架,Node是後端運行環境。安裝過程和node的特性就不講了,網上一大把。開發環境是VS2013.安裝了NTVS。node安裝完後,有時候須要設置下環境變量。在cmd目錄下輸入 node -v 若是顯示版本號,則說明安裝正確了。html
起始工程前端
在VS中新建項目,選擇JavaScript-->Node.js,選擇Express4的應用。java
爲了不一直Ctrl+C,安裝nodemon,文件更新,它會自動重啓,-g表示安裝成全局。node
npm install nodemon -g
修改routes文件夾下的index.js中的title爲ReadingClub。而後用cmd切到工程目錄,輸入nodemon啓動工程。 git
在瀏覽器裏面訪問lochost:3000 ,成功打開:angularjs
先看routes文件夾下面的index.js ,這就是一個簡單的路由,處理的路徑爲「/」,請求方式get,req表明的是request,res表明的response。github
render方法有兩個參數,「index」,表明的是要渲染的視圖模板名稱,這裏默認的視圖引擎是jade,然後面{title:'ReadingClub'}就是傳遞到視圖的數據模型。這裏和Asp.net MVC 的return View() 有些類似,而這裏的function就至關 Asp.net MVC中Controller的一個Action。View()默認是對應當前Action名稱的視圖。而render必須指定。mongodb
res 也能夠直接發回一個響應express
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;
首先看到不少的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中模塊分如下幾類:
核心模塊會優先加載,以相對或絕對路徑加載的模塊,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文件內容進行包裝,等因而每一個文件之間都進行了做用域的隔離。
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
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')));//靜態文件路徑
咱們看到在設置了路由以後,若是請求還沒返回則認爲頁面沒有找到,這個時候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》