Express是一個簡潔、靈活的 node.js Web 應用開發框架, 它提供一系列強大的特性,幫助開發者建立各類 Web 和移動設備應用。本文將詳細介紹express框架javascript
官網對Express的描述,它是一個基於 Node.js 平臺,快速、開放、極簡的 web 開發框架。優勢是易上手、高性能、擴展性強css
一、易上手:nodejs最初就是爲了開發高性能web服務器而被設計出來的,然而相對底層的API會讓很多新手望而卻步。express對web開發相關的模塊進行了適度的封裝,屏蔽了大量複雜繁瑣的技術細節,讓開發者只須要專一於業務邏輯的開發,極大的下降了入門和學習的成本html
二、高性能:Express僅在web應用相關的nodejs模塊上進行了適度的封裝和擴展,較大程度避免了過分封裝致使的性能損耗java
三、擴展性強:基於中間件的開發模式,使得express應用的擴展、模塊拆分很是簡單,既靈活,擴展性又強node
【安裝】mysql
安裝express前,首先安裝nodejs,接下來爲應用建立一個目錄,而後進入此目錄並將其做爲當前工做目錄jquery
$ mkdir myapp
$ cd myapp
經過 npm init 命令爲應用建立一個 package.json 文件git
$ npm init
此命令要求輸入幾個參數,例如應用的名稱和版本。 直接按「回車」鍵接受默認設置便可,下面這個除外:github
entry point: (index.js)
鍵入 app.js 或者所但願的名稱,這是當前應用的入口文件。若是但願採用默認的 index.js 文件名,只需按「回車」鍵便可web
接下來安裝 Express 並將其保存到依賴列表中:
$ npm install express --save
若是隻是臨時安裝 Express,不想將它添加到依賴列表中,只需略去 --save 參數便可:
$ npm install express
在項目根目錄下,新建一個啓動文件,假定叫作index.js,新建一個public文件夾,並在public目錄下,新建index.html
var express = require('express'); var app = express(); app.use(express.static(__dirname + '/public')); app.listen(8080);
運行index.js後,訪問http://localhost:8080
,它會在瀏覽器中打開public目錄的index.html文件
固然,也能夠在index.js之中,生成動態網頁
// index.js var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello world!'); }); app.listen(3000);
運行index.js文件後,會在本機的3000端口啓動一個網站,網頁顯示Hello World
啓動腳本index.js的app.get
方法,用於指定不一樣的訪問路徑所對應的回調函數,這叫作「路由」(routing)。上面代碼只指定了根目錄的回調函數,所以只有一個路由記錄。實際應用中,可能有多個路由記錄
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello world!'); }); app.get('/customer', function(req, res){ res.send('customer page'); }); app.get('/admin', function(req, res){ res.send('admin page'); }); app.listen(3000);
這時,最好就把路由放到一個單獨的文件中,好比新建一個routes子目錄
// routes/index.js module.exports = function (app) { app.get('/', function (req, res) { res.send('Hello world'); }); app.get('/customer', function(req, res){ res.send('customer page'); }); app.get('/admin', function(req, res){ res.send('admin page'); }); };
而後,原來的index.js就變成下面這樣
// index.js var express = require('express'); var app = express(); var routes = require('./routes')(app); app.listen(3000);
經過應用生成器工具 express
能夠快速建立一個應用的骨架
[注意]必定要使用全局模式安裝express-generator,不然沒法使用express命令
$ npm install express-generator -g
-h
選項能夠列出全部可用的命令行選項:
$ express -h Usage: express [options] [dir] Options: -h, --help output usage information -V, --version output the version number -e, --ejs add ejs engine support (defaults to jade) --hbs add handlebars engine support -H, --hogan add hogan.js engine support -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory
例如,下面的示例就是在當前工做目錄下建立一個命名爲 myapp 的應用
$ express myapp create : myapp create : myapp/package.json create : myapp/app.js create : myapp/public create : myapp/public/javascripts create : myapp/public/images create : myapp/routes create : myapp/routes/index.js create : myapp/routes/users.js create : myapp/public/stylesheets create : myapp/public/stylesheets/style.css create : myapp/views create : myapp/views/index.jade create : myapp/views/layout.jade create : myapp/views/error.jade create : myapp/bin create : myapp/bin/www
而後安裝全部依賴包:
$ cd myapp
$ npm instal
啓動這個應用(MacOS 或 Linux 平臺):
$ DEBUG=myapp npm start
Windows 平臺使用以下命令:
> set DEBUG=myapp & npm start
而後在瀏覽器中打開 http://localhost:3000/
網址就能夠看到這個應用了。i
經過 Express 應用生成器建立的應用通常都有以下目錄結構:
.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
7 directories, 9 files
Express框架創建在node.js內置的http模塊上。http模塊生成服務器的原始代碼以下
var http = require("http"); var app = http.createServer(function(request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.end("Hello world!"); }); app.listen(3000, "localhost");
上面代碼的關鍵是http模塊的createServer方法,表示生成一個HTTP服務器實例。該方法接受一個回調函數,該回調函數的參數,分別爲表明HTTP請求和HTTP迴應的request對象和response對象。
Express框架的核心是對http模塊的再包裝。上面的代碼用Express改寫以下
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello world!'); }); app.listen(3000);
比較兩段代碼,能夠看到它們很是接近。原來是用http.createServer
方法新建一個app實例,如今則是用Express的構造方法,生成一個Epress實例。二者的回調函數都是相同的。Express框架等於在http模塊之上,加了一箇中間層
【概述】
Express 是一個自身功能極簡,徹底是由路由和中間件構成一個的 web 開發框架:從本質上來講,一個 Express 應用就是在調用各類中間件
簡單說,中間件(middleware)就是處理HTTP請求的函數。它最大的特色就是,一箇中間件處理完,再傳遞給下一個中間件。App實例在運行過程當中,會調用一系列的中間件
每一箇中間件能夠從App實例,接收三個參數,依次爲request對象(表明HTTP請求)、response對象(表明HTTP迴應),next回調函數(表明下一個中間件)。每一箇中間件均可以對HTTP請求(request對象)進行加工,而且決定是否調用next方法,將request對象再傳給下一個中間件
中間件的功能包括:一、執行任何代碼;二、修改請求和響應對象;三、終結請求-響應循環;四、調用堆棧中的下一個中間件
若是當前中間件沒有終結請求-響應循環,則必須調用 next() 方法將控制權交給下一個中間件,不然請求就會掛起
一個不進行任何操做、只傳遞request對象的中間件,就是下面這樣
function uselessMiddleware(req, res, next) { next(); }
上面代碼的next就是下一個中間件。若是它帶有參數,則表明拋出一個錯誤,參數爲錯誤文本
function uselessMiddleware(req, res, next) { next('出錯了!'); }
拋出錯誤之後,後面的中間件將再也不執行,直到發現一個錯誤處理函數爲止
【分類】
Express 應用可以使用以下幾種中間件:一、應用級中間件;二、路由級中間件;三、錯誤處理中間件;四、內置中間件;五、第三方中間件
一、應用級中間件綁定到 app 對象 使用 app.use() 和 app.METHOD(),其中, METHOD 是須要處理的 HTTP 請求的方法,例如 GET, PUT, POST 等等,所有小寫
二、路由級中間件綁定的對象爲 express.Router()
三、錯誤處理中間件和其餘中間件定義相似,只是要使用 4 個參數,而不是 3 個,其簽名以下: (err, req, res, next)
app.use(function(err, req, res, next) { console.error(err.stack); res.status(500).send('Something broke!'); });
四、express.static
是 Express 惟一內置的中間件。它基於 serve-static,負責在 Express 應用中提託管靜態資源
五、經過使用第三方中間件從而爲 Express 應用增長更多功能。安裝所需功能的 node 模塊,並在應用中加載,能夠在應用級加載,也能夠在路由級加載。下面的例子安裝並加載了一個解析 cookie 的中間件: cookie-parser
$ npm install cookie-parser
var express = require('express'); var app = express(); var cookieParser = require('cookie-parser'); // 加載用於解析 cookie 的中間件 app.use(cookieParser());
【use方法】
use是express註冊中間件的方法,它返回一個函數。下面是一個連續調用兩個中間件的例子
var express = require("express"); var http = require("http"); var app = express(); app.use(function(request, response, next) { console.log("In comes a " + request.method + " to " + request.url); next(); }); app.use(function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello world!\n"); }); http.createServer(app).listen(1337);
上面代碼使用app.use
方法,註冊了兩個中間件。收到HTTP請求後,先調用第一個中間件,在控制檯輸出一行信息,而後經過next
方法,將執行權傳給第二個中間件,輸出HTTP迴應。因爲第二個中間件沒有調用next
方法,因此request對象就再也不向後傳遞了。
use
方法內部能夠對訪問路徑進行判斷,據此實現簡單的路由,根據不一樣的請求網址,返回不一樣的網頁內容
var express = require("express"); var http = require("http"); var app = express(); app.use(function(request, response, next) { if (request.url == "/") { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Welcome to the homepage!\n"); } else { next(); } }); app.use(function(request, response, next) { if (request.url == "/about") { response.writeHead(200, { "Content-Type": "text/plain" }); } else { next(); } }); app.use(function(request, response) { response.writeHead(404, { "Content-Type": "text/plain" }); response.end("404 error!\n"); }); http.createServer(app).listen(1337);
上面代碼經過request.url
屬性,判斷請求的網址,從而返回不一樣的內容。注意,app.use
方法一共登記了三個中間件,只要請求路徑匹配,就不會將執行權交給下一個中間件。所以,最後一箇中間件會返回404錯誤,即前面的中間件都沒匹配請求路徑,找不到所要請求的資源。
除了在回調函數內部判斷請求的網址,use方法也容許將請求網址寫在第一個參數。這表明,只有請求路徑匹配這個參數,後面的中間件纔會生效。無疑,這樣寫更加清晰和方便
app.use('/path', someMiddleware);
上面代碼表示,只對根目錄的請求,調用某個中間件。
所以,上面的代碼能夠寫成下面的樣子
var express = require("express"); var http = require("http"); var app = express(); app.use("/home", function(request, response, next) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Welcome to the homepage!\n"); }); app.use("/about", function(request, response, next) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Welcome to the about page!\n"); }); app.use(function(request, response) { response.writeHead(404, { "Content-Type": "text/plain" }); response.end("404 error!\n"); }); http.createServer(app).listen(1337);
上面介紹了, express.static
是 Express 惟一內置的中間件,負責在 Express 應用中提託管靜態資源,例如圖片、CSS、JavaScript 文件等
express.static(root, [options])
參數 root
指提供靜態資源的根目錄,可選的 options
參數擁有以下屬性
屬性 類型 缺省值 描述 dotfiles String 「ignore」 是否對外輸出文件名以點開頭的文件。可選值爲allow、deny和ignore etag Boolean true 是否啓用 etag 生成 extensions Array [] 設置文件擴展名備份選項 index Mixed 「index.html」 發送目錄索引文件,設置爲 false 禁用目錄索引。 lastModified Boolean true 設置Last-Modified頭爲文件在操做系統上的最後修改日期。可選值爲true或false maxAge Number 0 以毫秒或者其字符串格式設置 Cache-Control 頭的 max-age 屬性。 redirect Boolean true 當路徑爲目錄時,重定向至 「/」。 setHeaders Function 設置 HTTP 頭以提供文件的函數。
var options = { etag: false, extensions: ['htm', 'html'], index: false, maxAge: '1d', redirect: false, setHeaders: function (res, path, stat) { res.set('x-timestamp', Date.now()); } } app.use(express.static('public', options));
通常地,若是不須要特殊的設置,將靜態資源文件所在的目錄做爲參數傳遞給 express.static
中間件就能夠提供靜態資源文件的訪問了。例如,假設在 public
目錄放置了圖片、CSS 和 JavaScript 文件
app.use(express.static('public'));
如今,public
目錄下面的文件就能夠訪問了
http://localhost:3000/images/kitten.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/images/bg.png http://localhost:3000/hello.html
若是靜態資源存放在多個目錄下面,能夠屢次調用 express.static
中間件:
app.use(express.static('public'));
app.use(express.static('files'));
訪問靜態資源文件時,express.static
中間件會根據目錄添加的順序查找所需的文件。
若是但願全部經過 express.static
訪問的文件都存放在一個「虛擬(virtual)」目錄(即目錄根本不存在)下面,能夠經過爲靜態資源目錄指定一個掛載路徑的方式來實現,以下所示:
app.use('/static', express.static('public'));
如今,能夠經過帶有 「/static」 前綴的地址來訪問 public
目錄下面的文件了
http://localhost:3000/static/images/kitten.jpg http://localhost:3000/static/css/style.css http://localhost:3000/static/js/app.js http://localhost:3000/static/images/bg.png http://localhost:3000/static/hello.html
【cookie-parser()】
用於解析cookie的中間件,添加中間後,req具有cookies屬性。經過req.cookies.xxx
能夠訪問cookie的值
$ npm install cookie-parser
var cookieParser = require('cookie-parser') app.use(cookieParser(secret, options))
secret
是可選參數,用於對cookie進行簽名
,經過它能夠判斷出客戶是否修改了cookie,這是處於安全考慮,這個參數是任意字符串
options
可選參數,是一個json對象,可選項包括path、expires、maxAge、domain、secure、httpOnly
var express = require('express') var cookieParser = require('cookie-parser') var app = express() app.use(cookieParser()) app.get('/', function(req, res) { console.log('Cookies: ', req.cookies) }) app.listen(8080)
【express-session】
session運行在服務器端,當客戶端第一次訪問服務器時,能夠將客戶的登陸信息保存。 當客戶訪問其餘頁面時,能夠判斷客戶的登陸狀態,作出提示,至關於登陸攔截。session能夠和Redis或者數據庫等結合作持久化操做,當服務器掛掉時也不會致使某些客戶信息(購物車)丟失。
當瀏覽器訪問服務器併發送第一次請求時,服務器端會建立一個session對象,生成一個相似於key,value的鍵值對, 而後將key(cookie)返回到瀏覽器(客戶)端,瀏覽器下次再訪問時,攜帶key(cookie),找到對應的session(value)
。客戶的信息都保存在session中
$ npm install express-session
var express = require('express') var session = require('express-session') var app = express() app.use(session(options))
options 經常使用選項以下:
name - 默認'connect.sid',可自定義
store - session 儲存器實例
secret - 用於對cookie進行簽名
,經過它能夠判斷出客戶是否修改了cookie,這是處於安全考慮,這個參數是任意字符串
cookie - 對session cookie的設置 。默認值 { path: '/', httpOnly: true, secure: false, maxAge: null }
genid - 是個函數,調用它來生成一個新的會話ID。 (默認:使用UID2庫)
rolling - 強制對每一個響應的Cookie,重置到期日期。 (默認:false)
resave - 每一次都從新保存,即便沒修改過(默認:true)
proxy - ture/false,是否支持trust proxy
,,須要設置 app.enable('trust proxy');
通常來講,無需設置
經常使用方法以下:
Session.destroy()
:刪除session,當檢測到客戶端關閉時調用
Session.reload()
:當session有修改時,刷新session
Session.regenerate()
:將已有session初始化
Session.save()
:保存session
var express = require('express'); var cookieParser = require('cookie-parser'); var session = require('express-session'); app.use(cookieParser('sessiontest')); app.use(session({ secret: 'sessiontest',//與cookieParser中的一致 resave: true, saveUninitialized:true }));
//修改router/index.js,第一次請求時保存一條用戶信息。 router.get('/', function(req, res, next) { var user={ name:"Chen-xy", age:"22", address:"bj" } req.session.user=user; res.render('index', { title: 'the test for nodejs session' , name:'sessiontest' }); });
//修改router/users.js,判斷用戶是否登錄。 router.get('/', function(req, res, next) { if(req.session.user){ var user=req.session.user; var name=user.name; res.send('你好'+name+',歡迎來到個人家園。'); }else{ res.send('你尚未登陸,先登陸下再試試!'); } });
【serve-favicon】
設置網站的 favicon圖標
$ npm install serve-favicon
var express = require('express') var favicon = require('serve-favicon') var path = require('path') var app = express() app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) // Add your routes here, etc. app.listen(3000)
【body-parser】
bodyParser用於解析客戶端請求的body中的內容,內部使用JSON編碼處理,url編碼處理以及對於文件的上傳處理
$ npm install body-parser
var bodyParser = require('body-parser')
一、底層中間件用法:這將攔截和解析全部的請求;也即這種用法是全局的。
var express = require('express') var bodyParser = require('body-parser') var app = express() // parse application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: false })) // parse application/json app.use(bodyParser.json()) app.use(function (req, res) { res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') res.end(JSON.stringify(req.body, null, 2)) })
use方法調用body-parser實例;且use方法沒有設置路由路徑;這樣的body-parser實例就會對該app全部的請求進行攔截和解析
二、特定路由下的中間件用法:這種用法是針對特定路由下的特定請求的,只有請求該路由時,中間件纔會攔截和解析該請求;也即這種用法是局部的;也是最經常使用的一個方式
var express = require('express') var bodyParser = require('body-parser') var app = express() // create application/json parser var jsonParser = bodyParser.json() // create application/x-www-form-urlencoded parser var urlencodedParser = bodyParser.urlencoded({ extended: false }) // POST /login gets urlencoded bodies app.post('/login', urlencodedParser, function (req, res) { if (!req.body) return res.sendStatus(400) res.send('welcome, ' + req.body.username) }) // POST /api/users gets JSON bodies app.post('/api/users', jsonParser, function (req, res) { if (!req.body) return res.sendStatus(400) // create user in req.body })
express的post(或者get)方法調用body-parser實例;且該方法有設置路由路徑;這樣的body-parser實例就會對該post(或者get)的請求進行攔截和解析
三、設置Content-Type 屬性;用於修改和設定中間件解析的body內容類型
// parse various different custom JSON types as JSON app.use(bodyParser.json({ type: 'application/*+json' }); // parse some custom thing into a Buffer app.use(bodyParser.raw({ type: 'application/vnd.custom-type' })); // parse an HTML body into a string app.use(bodyParser.text({ type: 'text/html' }));
【morgan】
Mogran是一個node.js關於http請求的express默認的日誌中間件
npm install morgan
在basic.js
中添加以下代碼
var express = require('express'); var app = express(); var morgan = require('morgan'); app.use(morgan('short')); app.use(function(req, res, next){ res.send('ok'); }); app.listen(3000);
node basic.js
運行程序,並在瀏覽器裏訪問 http://127.0.0.1:3000 ,打印日誌以下
::1 - GET / HTTP/1.1 200 2 - 3.157 ms ::1 - GET / HTTP/1.1 304 - - 0.784 ms
morgan支持stream配置項,能夠經過它來實現將日誌落地的效果,代碼以下:
var express = require('express'); var app = express(); var morgan = require('morgan'); var fs = require('fs'); var path = require('path'); var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), {flags: 'a'}); app.use(morgan('short', {stream: accessLogStream})); app.use(function(req, res, next){ res.send('ok'); }); app.listen(3000);
morgan的API很是少,使用頻率最高的就是morgan()
,做用是返回一個express日誌中間件
morgan(format, options)
參數說明以下:
format:可選,morgan與定義了幾種日誌格式,每種格式都有對應的名稱,好比combined、short等,默認是default
options:可選,配置項,包含stream(經常使用)、skip、immediate
stream:日誌的輸出流配置,默認是process.stdout
skip:是否跳過日誌記錄
immediate:布爾值,默認是false。當爲true時,一收到請求,就記錄日誌;若是爲false,則在請求返回後,再記錄日誌
【路由方法】
針對不一樣的請求,Express提供了use方法的一些別名,這些別名是和 HTTP 請求對應的路由方法: get
、post
、put
、head
、delete
、options
、trace
、copy
、lock
、mkcol
、move
、purge
、propfind
、proppatch
、unlock
、report
、mkactivity
、checkout
、merge
、m-search
、notify
、subscribe
、unsubscribe
、patch
、search
和 connect
app.all()
是一個特殊的路由方法,沒有任何 HTTP 方法與其對應,它的做用是對於一個路徑上的全部請求加載中間件
有些路由方法名不是合規的 JavaScript 變量名,此時使用括號記法,好比 app['m-search']('/', function ...
var express = require("express"); var http = require("http"); var app = express(); app.all("*", function(request, response, next) { response.writeHead(200, { "Content-Type": "text/plain" }); next(); }); app.get("/", function(request, response) { response.end("Welcome to the homepage!"); }); app.get("/about", function(request, response) { response.end("Welcome to the about page!"); }); app.get("*", function(request, response) { response.end("404!"); }); http.createServer(app).listen(1337);
上面代碼的all方法表示,全部請求都必須經過該中間件,參數中的「*」表示對全部路徑有效。get方法則是隻有GET動詞的HTTP請求經過該中間件,它的第一個參數是請求的路徑。因爲get方法的回調函數沒有調用next方法,因此只要有一箇中間件被調用了,後面的中間件就不會再被調用了
【路由路徑】
路由方法的第一個參數,都是請求的路徑,稱爲路由路徑,它能夠是字符串、字符串模式或者正則表達式
一、字符串匹配
// 匹配 /about 路徑的請求 app.get('/about', function (req, res) { res.send('about'); });
二、字符串模式匹配
// 匹配 acd 和 abcd app.get('/ab?cd', function(req, res) { res.send('ab?cd'); });
三、正則表達式匹配
// 匹配任何路徑中含有 a 的路徑: app.get(/a/, function(req, res) { res.send('/a/'); });
【路由句柄】
能夠爲請求處理提供多個回調函數,其行爲相似中間件。惟一的區別是這些回調函數可能調用 next('route')
方法而略過其餘路由回調函數。能夠利用該機制爲路由定義前提條件,若是在現有路徑上繼續執行沒有意義,則可將控制權交給剩下的路徑
路由句柄有多種形式,能夠是一個函數、一個函數數組,或者是二者混合
一、使用一個回調函數處理路由
app.get('/example/a', function (req, res) { res.send('Hello from A!'); });
二、使用多個回調函數處理路由
app.get('/example/b', function (req, res, next) { console.log('response will be sent by the next function ...'); next(); }, function (req, res) { res.send('Hello from B!'); });
三、使用回調函數數組處理路由
var cb0 = function (req, res, next) { console.log('CB0'); next(); } var cb1 = function (req, res, next) { console.log('CB1'); next(); } var cb2 = function (req, res) { res.send('Hello from C!'); } app.get('/example/c', [cb0, cb1, cb2]);
四、混合使用函數和函數數組處理路由
var cb0 = function (req, res, next) { console.log('CB0'); next(); } var cb1 = function (req, res, next) { console.log('CB1'); next(); } app.get('/example/d', [cb0, cb1], function (req, res, next) { console.log('response will be sent by the next function ...'); next(); }, function (req, res) { res.send('Hello from D!'); });
【鏈式路由句柄】
可以使用 app.route()
建立路由路徑的鏈式路由句柄。因爲路徑在一個地方指定,這樣作有助於建立模塊化的路由,並且減小了代碼冗餘和拼寫錯誤
app.route('/book') .get(function(req, res) { res.send('Get a random book'); }) .post(function(req, res) { res.send('Add a book'); }) .put(function(req, res) { res.send('Update the book'); });
從Express 4.0開始,路由器功能成了一個單獨的組件Express.Router
。它好像小型的express應用程序同樣,有本身的use、get、param和route方法
可以使用 express.Router
類建立模塊化、可掛載的路由句柄。Router
實例是一個完整的中間件和路由系統,所以常稱其爲一個 「mini-app」
【基本用法】
首先,Express.Router
是一個構造函數,調用後返回一個路由器實例。而後,使用該實例的HTTP動詞方法,爲不一樣的訪問路徑,指定回調函數;最後,掛載到某個路徑
var express = require('express'); var router = express.Router(); router.get('/', function(req, res) { res.send('首頁'); }); router.get('/about', function(req, res) { res.send('關於'); }); app.use('/', router);
上面代碼先定義了兩個訪問路徑,而後將它們掛載到根目錄。若是最後一行改成app.use(‘/app’, router),則至關於爲/app
和/app/about
這兩個路徑,指定了回調函數。
這種路由器能夠自由掛載的作法,爲程序帶來了更大的靈活性,既能夠定義多個路由器實例,也能夠爲將同一個路由器實例掛載到多個路徑
【router.route方法】
router實例對象的route方法,能夠接受訪問路徑做爲參數
var express = require('express'); var router = express.Router(); router.route('/api') .post(function(req, res) { // ... }) .get(function(req, res) { Bear.find(function(err, bears) { if (err) res.send(err); res.json(bears); }); }); app.use('/', router);
【router中間件】
use方法爲router對象指定中間件,在數據正式發給用戶以前,對數據進行處理。下面是一箇中間件的例子
router.use(function(req, res, next) { console.log(req.method, req.url); next(); });
上面代碼中,回調函數的next參數,表示接受其餘中間件的調用。函數體中的next(),表示將數據傳遞給下一個中間件
[注意]中間件放置順序很重要,等同於執行順序。並且,中間件必須放在HTTP動詞方法以前,不然不會執行
【對路徑參數的處理】
router對象的param方法用於路徑參數的處理
router.param('name', function(req, res, next, name) { // 對name進行驗證或其餘處理…… console.log(name); req.name = name; next(); }); router.get('/hello/:name', function(req, res) { res.send('hello ' + req.name + '!'); });
上面代碼中,get方法爲訪問路徑指定了name參數,param方法則是對name參數進行處理
[注意]param方法必須放在HTTP動詞方法以前
【實例】
下面的實例程序建立了一個路由模塊,並加載了一箇中間件,定義了一些路由,而且將它們掛載至應用路徑上
在 app 目錄下建立名爲 birds.js
的文件,內容以下:
var express = require('express'); var router = express.Router(); // 該路由使用的中間件 router.use(function timeLog(req, res, next) { console.log('Time: ', Date.now()); next(); }); // 定義網站主頁的路由 router.get('/', function(req, res) { res.send('Birds home page'); }); // 定義 about 頁面的路由 router.get('/about', function(req, res) { res.send('About birds'); }); module.exports = router;
而後在應用中加載路由模塊:
var birds = require('./birds'); ... app.use('/birds', birds);
應用便可處理髮自 /birds
和 /birds/about
的請求,而且調用爲該路由指定的 timeLog
中間件
response對象包含如下9個方法,response對象的方法向客戶端返回響應,終結請求響應的循環。若是在路由句柄中一個方法也不調用,來自客戶端的請求會一直掛起
方法 描述
res.download() 提示下載文件。
res.end() 終結響應處理流程。
res.json() 發送一個 JSON 格式的響應。
res.jsonp() 發送一個支持 JSONP 的 JSON 格式的響應。
res.redirect() 重定向請求。
res.render() 渲染視圖模板。
res.send() 發送各類類型的響應。
res.sendFile() 以八位字節流的形式發送文件。
res.sendStatus() 設置響應狀態代碼,並將其以字符串形式做爲響應體的一部分發送。
一、response.download方法
//下載路徑爲'/report-12345.pdf'的文件 res.download('/report-12345.pdf'); //下載路徑爲'/report-12345.pdf'的文件,並將文件命名爲 'report.pdf' res.download('/report-12345.pdf', 'report.pdf'); //下載路徑爲'/report-12345.pdf'的文件,將文件命名爲 'report.pdf',而且回調 res.download('/report-12345.pdf', 'report.pdf', function(err){ if (err) { } else { } });
二、response.end方法
//終結響應處理流程 res.end(); //設置響應碼爲404,並終結響應處理流程 res.status(404).end();
三、response.json方法
res.json(null) res.json({ user: 'tobi' }) res.status(500).json({ error: 'message' })
四、response.jsonp方法
res.jsonp(null) res.jsonp({ user: 'tobi' }) res.status(500).jsonp({ error: 'message' })
五、response.redirect方法
res.redirect('/foo/bar'); res.redirect('http://example.com'); res.redirect(301, 'http://example.com'); res.redirect('../login');
六、response.render方法
res.render('index'); res.render('index', function(err, html) { res.send(html); }); res.render('user', { name: 'Tobi' }, function(err, html) { // ... });
七、response.send方法
res.send(new Buffer('whoop')); res.send({ some: 'json' }); res.send('<p>some html</p>'); res.status(404).send('Sorry, we cannot find that!'); res.status(500).send({ error: 'something blew up' });
八、response.sendFile方法
response.sendFile("/path/to/anime.mp4");
九、response.sendStatus方法
res.sendStatus(200); // 'OK' res.sendStatus(403); // 'Forbidden' res.sendStatus(404); // 'Not Found' res.sendStatus(500); // 'Internal Server Error'
【req.params】
// GET /user/tj req.params.name // => "tj" // GET /file/javascripts/jquery.js req.params[0] // => "javascripts/jquery.js"
【req.query】
// GET /search?q=tobi+ferret req.query.q // => "tobi ferret" // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse req.query.order // => "desc" req.query.shoe.color // => "blue" req.query.shoe.type // => "converse"
【req.body】
// POST user[name]=tobi&user[email]=tobi@learnboost.com req.body.user.name // => "tobi" req.body.user.email // => "tobi@learnboost.com" // POST { "name": "tobi" } req.body.name // => "tobi"
【req.param(name)】
// ?name=tobi req.param('name') // => "tobi" // POST name=tobi req.param('name') // => "tobi" // /user/tobi for /user/:name req.param('name') // => "tobi"
【req.cookies】
// Cookie: name=tj req.cookies.name // => "tj"
【req.ip】
req.ip // => "127.0.0.1"
【req.path
】
// example.com/users?sort=desc req.path // => "/users"
【req.host】
// Host: "example.com:3000" req.host // => "example.com"
【set方法】
set方法用於指定變量的值
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
上面代碼使用set方法,爲系統變量「views」和「view engine」指定值
【get方法】
除了做爲use()方法的別名用法外,get方法還用於獲取變量的值,與set方法相對應
app.get('title'); // => undefined app.set('title', 'My Site'); app.get('title'); // => "My Site"
【app.enable(name)】
將設置項 name 的值設爲 true
app.enable('trust proxy'); app.get('trust proxy'); // => true
【app.disable(name)】
將設置項 name 的值設爲 false
app.disable('trust proxy'); app.get('trust proxy'); // => false
【app.enabled(name)】
檢查設置項 name 是否已啓用
app.enabled('trust proxy'); // => false app.enable('trust proxy'); app.enabled('trust proxy'); // => true
【app.disabled(name)】
檢查設置項 name 是否已禁用
app.disabled('trust proxy'); // => true app.enable('trust proxy'); app.disabled('trust proxy'); // => false
【app.engine(ext, callback)】
註冊模板引擎的 callback 用來處理 ext 擴展名的文件
默認狀況下, 根據文件擴展名 require() 加載相應的模板引擎。 好比想渲染一個 「foo.jade」 文件,Express 會在內部執行下面的代碼,而後會緩存 require() ,這樣就能夠提升後面操做的性能
app.engine('jade', require('jade').__express);
那些沒有提供 .__express 的或者想渲染一個文件的擴展名與模板引擎默認的不一致的時候,也能夠用這個方法。好比想用EJS模板引擎來處理 「.html」 後綴的文件:
app.engine('html', require('ejs').renderFile);
這個例子中 EJS 提供了一個 .renderFile() 方法和 Express 預期的格式: (path, options, callback) 一致, 所以能夠在內部給這個方法取一個別名 ejs.__express ,這樣就可使用 「.ejs」 擴展而不須要作任何改動
有些模板引擎沒有遵循這種轉換, 這裏有一個小項目 consolidate.js專門把全部的node流行的模板引擎進行了包裝,這樣它們在 Express 內部看起來就同樣了。
var engines = require('consolidate'); app.engine('haml', engines.haml); app.engine('html', engines.hogan);
【app.locals】
應用程序本地變量會附加給全部的在這個應用程序內渲染的模板。這是一個很是有用的模板函數,就像應用程序級數據同樣
app.locals.title = 'My App';
app.locals.strftime = require('strftime');
app.locals 對象是一個 JavaScript Function,執行的時候它會把屬性合併到它自身,提供了一種簡單展現已有對象做爲本地變量的方法。
app.locals({ title: 'My App', phone: '1-250-858-9990', email: 'me@myapp.com' }); app.locals.title // => 'My App' app.locals.email // => 'me@myapp.com'
app.locals 對象最終會是一個 Javascript 函數對象,不可使用 Functions 和 Objects 內置的屬性,好比 name、apply、bind、call、arguments、length、constructor。
app.locals({name: 'My App'}); app.locals.name // => 返回 'app.locals' 而不是 'My App' (app.locals 是一個函數 !) // => 若是 name 變量用在一個模板裏,則返回一個 ReferenceError
默認狀況下Express只有一個應用程序級本地變量,它是 settings
app.set('title', 'My App'); // 在 view 裏使用 settings.title
【app.render(view, [options], callback)】
渲染 view , 回調函數 callback 用來處理返回的渲染後的字符串。這個是 res.render() 的應用程序級版本,它們的行爲是同樣的。
app.render('email', function(err, html){ // ... }); app.render('email', { name: 'Tobi' }, function(err, html){ // ... });
【app.listen()】
在給定的主機和端口上監聽請求,這個和 node 文檔中的 http.Server#listen() 是一致的。
var express = require('express'); var app = express(); app.listen(3000);
express() 返回的 app 其實是一個 JavaScript Function,它被設計爲傳給 node 的 http servers 做爲處理請求的回調函數。由於 app 不是從 HTTP 或者 HTTPS 繼承來的,它只是一個簡單的回調函數,能夠以同一份代碼同時處理 HTTP 和 HTTPS 版本的服務。
var express = require('express'); var https = require('https'); var http = require('http'); var app = express(); http.createServer(app).listen(80); https.createServer(options, app).listen(443);
app.listen() 方法只是一個快捷方法,若是想使用 HTTPS ,或者同時提供 HTTP 和 HTTPS ,可使用上面的代碼。
app.listen = function(){ var server = http.createServer(this); return server.listen.apply(server, arguments); };
使用Express搭建HTTPS加密服務器很簡單
var fs = require('fs'); var options = { key: fs.readFileSync('E:/ssl/myserver.key'), cert: fs.readFileSync('E:/ssl/myserver.crt'), passphrase: '1234' }; var https = require('https'); var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World Expressjs'); }); var server = https.createServer(options, app); server.listen(8084); console.log('Server is running on port 8084');
須要在應用中進行以下設置才能讓 Express 渲染模板文件:
views, 放模板文件的目錄,好比: app.set('views', './views')
view engine, 模板引擎,好比: app.set('view engine', 'jade')
而後安裝相應的模板引擎 npm 軟件包
$ npm install jade --save
一旦 view engine
設置成功,就不須要顯式指定引擎,或者在應用中加載模板引擎模塊,Express 已經在內部加載,以下所示
app.set('view engine', 'jade');
在 views
目錄下生成名爲 index.jade
的 Jade 模板文件,內容以下:
html head title!= title body h1!= message
而後建立一個路由渲染 index.jade
文件。若是沒有設置 view engine
,須要指明視圖文件的後綴,不然就會遺漏它
app.get('/', function (req, res) { res.render('index', { title: 'Hey', message: 'Hello there!'}); });
此時向主頁發送請求,「index.jade」 會被渲染爲 HTML
爲 Express 應用添加鏈接數據庫的能力,只須要加載相應數據庫的 Node.js 驅動便可。這裏簡要介紹如何爲 Express 應用添加和使用一些經常使用的數據庫 Node 模塊
【mysql】
$ npm install mysql
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'dbuser', password : 's3kreee7' }); connection.connect(); connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) { if (err) throw err; console.log('The solution is: ', rows[0].solution); }); connection.end();
【MongoDB】
$ npm install mongoskin
var db = require('mongoskin').db('localhost:27017/animals'); db.collection('mamals').find().toArray(function(err, result) { if (err) throw err; console.log(result); });
首先,在網頁插入上傳文件的表單
<form action="/pictures/upload" method="POST" enctype="multipart/form-data">
Select an image to upload:
<input type="file" name="image">
<input type="submit" value="Upload Image">
</form>
而後,服務器腳本創建指向/upload
目錄的路由。這時能夠安裝multer模塊,它提供了上傳文件的許多功能
var express = require('express'); var router = express.Router(); var multer = require('multer'); var uploading = multer({ dest: __dirname + '../public/uploads/', // 設定限制,每次最多上傳1個文件,文件大小不超過1MB limits: {fileSize: 1000000, files:1}, }) router.post('/upload', uploading, function(req, res) {}) module.exports = router
上面代碼是上傳文件到本地目錄。下面是上傳到Amazon S3的例子。
首先,在S3上面新增CORS配置文件
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
上面的配置容許任意電腦向你的bucket發送HTTP請求。
而後,安裝aws-sdk
$ npm install aws-sdk --save
下面是服務器腳本
var express = require('express'); var router = express.Router(); var aws = require('aws-sdk'); router.get('/', function(req, res) { res.render('index') }) var AWS_ACCESS_KEY = 'your_AWS_access_key' var AWS_SECRET_KEY = 'your_AWS_secret_key' var S3_BUCKET = 'images_upload' router.get('/sign', function(req, res) { aws.config.update({accessKeyId: AWS_ACCESS_KEY, secretAccessKey: AWS_SECRET_KEY}); var s3 = new aws.S3() var options = { Bucket: S3_BUCKET, Key: req.query.file_name, Expires: 60, ContentType: req.query.file_type, ACL: 'public-read' } s3.getSignedUrl('putObject', options, function(err, data){ if(err) return res.send('Error with S3') res.json({ signed_request: data, url: 'https://s3.amazonaws.com/' + S3_BUCKET + '/' + req.query.file_name }) }) }) module.exports = router
上面代碼中,用戶訪問/sign
路徑,正確登陸後,會收到一個JSON對象,裏面是S3返回的數據和一個暫時用來接收上傳文件的URL,有效期只有60秒。
瀏覽器代碼以下
// HTML代碼爲 // <br>Please select an image // <input type="file" id="image"> // <br> // <img id="preview"> document.getElementById("image").onchange = function() { var file = document.getElementById("image").files[0] if (!file) return sign_request(file, function(response) { upload(file, response.signed_request, response.url, function() { document.getElementById("preview").src = response.url }) }) } function sign_request(file, done) { var xhr = new XMLHttpRequest() xhr.open("GET", "/sign?file_name=" + file.name + "&file_type=" + file.type) xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { var response = JSON.parse(xhr.responseText) done(response) } } xhr.send() } function upload(file, signed_request, url, done) { var xhr = new XMLHttpRequest() xhr.open("PUT", signed_request) xhr.setRequestHeader('x-amz-acl', 'public-read') xhr.onload = function() { if (xhr.status === 200) { done() } } xhr.send(file) }
上面代碼首先監聽file控件的change事件,一旦有變化,就先向服務器要求一個臨時的上傳URL,而後向該URL上傳文件
【靜態網頁模板】
在項目目錄之中,創建一個子目錄views,用於存放網頁模板。
假定這個項目有三個路徑:根路徑(/)、自我介紹(/about)和文章(/article)。那麼,app.js能夠這樣寫:
var express = require('express'); var app = express(); app.get('/', function(req, res) { res.sendfile('./views/index.html'); }); app.get('/about', function(req, res) { res.sendfile('./views/about.html'); }); app.get('/article', function(req, res) { res.sendfile('./views/article.html'); }); app.listen(3000);
上面代碼表示,三個路徑分別對應views目錄中的三個模板:index.html、about.html和article.html。另外,向服務器發送信息的方法,從send變成了sendfile,後者專門用於發送文件。
假定index.html的內容以下:
<html> <head> <title>首頁</title> </head> <body> <h1>Express Demo</h1> <header> <p> <a href="/">首頁</a> - <a href="/about">自我介紹</a> - <a href="/article">文章</a> </p> </header> </body> </html>
about.html內容以下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
about
</body>
</html>
article.html內容以下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
article
</body>
</html>
運行app.js後,訪問http://localhost:3000/結果以下
【動態網頁模板】
下面來製做一個動態網頁網站,以使用ejs引擎爲例
npm install ejs
將view engine修改成ejs,並將模板的後綴修改成.html
var express = require('express'); var app = express(); var ejs = require('ejs'); // 指定模板文件的後綴名爲html app.set('view engine', 'html'); //運行ejs引擎讀取html文件 app.engine('.html', ejs.__express); app.get('/', function (req, res){ res.render('index'); }); app.get('/about', function(req, res) { res.render('about'); }); app.get('/article', function(req, res) { res.render('article'); });
接下來,新建數據腳本。渲染是指將數據代入模板的過程。實際運用中,數據都是保存在數據庫之中的,這裏爲了簡化問題,假定數據保存在一個腳本文件中
在項目目錄中,新建一個文件blog.js,用於存放數據。blog.js的寫法符合CommonJS規範,使得它能夠被require語句加載
// blog.js文件 var entries = [ {"id":1, "title":"第一篇", "body":"正文", "published":"7/2/2017"}, {"id":2, "title":"第二篇", "body":"正文", "published":"7/3/2017"}, {"id":3, "title":"第三篇", "body":"正文", "published":"7/4/2017"}, {"id":4, "title":"第四篇", "body":"正文", "published":"7/5/2017"}, {"id":5, "title":"第五篇", "body":"正文", "published":"7/10/2017"}, {"id":6, "title":"第六篇", "body":"正文", "published":"7/12/2017"} ]; exports.getBlogEntries = function (){ return entries; } exports.getBlogEntry = function (id){ for(var i=0; i < entries.length; i++){ if(entries[i].id == id) return entries[i]; } }
新建header.html和footer.html
<!-- views/header.html文件 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title><%=title %></title> </head> <body> <!-- views/footer.html文件 --> <footer> <p> <a href="/">首頁</a> <a href="/about">自我介紹</a> </p> </footer> </body> </html>
接着,新建模板文件index.html
<!-- views/index.html文件 --> <% include header.html %> <h1>文章列表</h1> <% for(var i=0; i < entries.length; i++){ %> <p> <a href="/article/<%=entries[i].id %>"><%=entries[i].title %></a> <br> <span>時間: <%=entries[i].published %></span> </p> <% } %> <% include footer.html %>
新建模板文件about.html
<!-- views/about.html文件 --> <% include header.html %> <h1><%=title %> </h1> <p>正文</p> <% include footer.html %>
新建模板文件article.html
<!-- views/article.html文件 --> <% include header.html %> <h1><%=blog.title %></h1> <p>時間: <%=blog.published %></p> <p><%=blog.body %></p> <% include footer.html %>
最後,改寫app.js文件
var express = require('express'); var app = express(); var ejs = require('ejs'); // 加載數據模塊 var blogEngine = require('./blog'); app.set('view engine', 'html'); app.engine('html', ejs.__express); app.get('/', function(req, res) { res.render('index',{title:"最近文章", entries:blogEngine.getBlogEntries()}); }); app.get('/about', function(req, res) { res.render('about', {title:"自我介紹"}); }); app.get('/article/:id', function(req, res) { var entry = blogEngine.getBlogEntry(req.params.id); res.render('article',{title:entry.title, blog:entry}); }); app.listen(3000);
上面代碼中的render方法,如今加入了第二個參數,表示模板變量綁定的數據。
如今重啓node服務器,而後訪問http://127.0.0.1:3000來查看結果