概述
Express是目前最流行的基于Node.js的Web開(kāi)發(fā)框架,可以快速地搭建一個(gè)完整功能的網(wǎng)站。
Express上手非常簡(jiǎn)單,首先新建一個(gè)項(xiàng)目目錄,假定叫做hello-world。
$ mkdir hello-world
進(jìn)入該目錄,新建一個(gè)package.json文件,內(nèi)容如下。
{
"name": "hello-world",
"description": "hello world test app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.x"
}
}
上面代碼定義了項(xiàng)目的名稱、描述、版本等,并且指定需要4.0版本以上的Express。
然后,就可以安裝了。
$ npm install
執(zhí)行上面的命令以后,在項(xiàng)目根目錄下,新建一個(gè)啟動(dòng)文件,假定叫做index.js。
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(8080);
1.png
然后,運(yùn)行上面的啟動(dòng)腳本。
$ node index
現(xiàn)在就可以訪問(wèn)http://localhost:8080,它會(huì)在瀏覽器中打開(kāi)當(dāng)前目錄的public子目錄(嚴(yán)格來(lái)說(shuō),是打開(kāi)public目錄的index.html文件)。如果public目錄之中有一個(gè)圖片文件my_image.png,那么可以用http://localhost:8080/my_image.png訪問(wèn)該文件。
你也可以在index.js之中,生成動(dòng)態(tài)網(wǎng)頁(yè)。

然后,在命令行下運(yùn)行啟動(dòng)腳本,就可以在瀏覽器中訪問(wèn)項(xiàng)目網(wǎng)站了。
$ node index
上面代碼會(huì)在本機(jī)的3000端口啟動(dòng)一個(gè)網(wǎng)站,網(wǎng)頁(yè)顯示Hello World。
啟動(dòng)腳本index.js的app.get方法,用于指定不同的訪問(wèn)路徑所對(duì)應(yīng)的回調(diào)函數(shù),這叫做“路由”(routing)。上面代碼只指定了根目錄的回調(diào)函數(shù),因此只有一個(gè)路由記錄。實(shí)際應(yīng)用中,可能有多個(gè)路由記錄。

這時(shí),最好就把路由放到一個(gè)單獨(dú)的文件中,比如新建一個(gè)routes子目錄。

然后,原來(lái)的index.js就變成下面這樣。
// index.js
var express = require('express');
var app = express();
var routes = require('./routes')(app);
app.listen(3000);
運(yùn)行原理
底層:http模塊
Express框架建立在node.js內(nèi)置的http模塊上。http模塊生成服務(wù)器的原始代碼如下。
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");
上面代碼的關(guān)鍵是http模塊的createServer方法,表示生成一個(gè)HTTP服務(wù)器實(shí)例。該方法接受一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)的參數(shù),分別為代表HTTP請(qǐng)求和HTTP回應(yīng)的request對(duì)象和response對(duì)象。
Express框架的核心是對(duì)http模塊的再包裝。上面的代碼用Express改寫(xiě)如下。
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello world!');
});
app.listen(3000);
比較兩段代碼,可以看到它們非常接近。原來(lái)是用http.createServer方法新建一個(gè)app實(shí)例,現(xiàn)在則是用Express的構(gòu)造方法,生成一個(gè)Epress實(shí)例。兩者的回調(diào)函數(shù)都是相同的。Express框架等于在http模塊之上,加了一個(gè)中間層。
什么是中間件
簡(jiǎn)單說(shuō),中間件(middleware)就是處理HTTP請(qǐng)求的函數(shù)。它最大的特點(diǎn)就是,一個(gè)中間件處理完,再傳遞給下一個(gè)中間件。App實(shí)例在運(yùn)行過(guò)程中,會(huì)調(diào)用一系列的中間件。
每個(gè)中間件可以從App實(shí)例,接收三個(gè)參數(shù),依次為request對(duì)象(代表HTTP請(qǐng)求)、response對(duì)象(代表HTTP回應(yīng)),next回調(diào)函數(shù)(代表下一個(gè)中間件)。每個(gè)中間件都可以對(duì)HTTP請(qǐng)求(request對(duì)象)進(jìn)行加工,并且決定是否調(diào)用next方法,將request對(duì)象再傳給下一個(gè)中間件。
一個(gè)不進(jìn)行任何操作、只傳遞request對(duì)象的中間件,就是下面這樣。
function uselessMiddleware(req, res, next) {
next();
}
上面代碼的next就是下一個(gè)中間件。如果它帶有參數(shù),則代表拋出一個(gè)錯(cuò)誤,參數(shù)為錯(cuò)誤文本。
function uselessMiddleware(req, res, next) {
next('出錯(cuò)了!');
}
拋出錯(cuò)誤以后,后面的中間件將不再執(zhí)行,直到發(fā)現(xiàn)一個(gè)錯(cuò)誤處理函數(shù)為止。
use方法
use是express注冊(cè)中間件的方法,它返回一個(gè)函數(shù)。下面是一個(gè)連續(xù)調(diào)用兩個(gè)中間件的例子。
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方法,注冊(cè)了兩個(gè)中間件。收到HTTP請(qǐng)求后,先調(diào)用第一個(gè)中間件,在控制臺(tái)輸出一行信息,然后通過(guò)next方法,將執(zhí)行權(quán)傳給第二個(gè)中間件,輸出HTTP回應(yīng)。由于第二個(gè)中間件沒(méi)有調(diào)用next方法,所以request對(duì)象就不再向后傳遞了。
use方法內(nèi)部可以對(duì)訪問(wèn)路徑進(jìn)行判斷,據(jù)此就能實(shí)現(xiàn)簡(jiǎn)單的路由,根據(jù)不同的請(qǐng)求網(wǎng)址,返回不同的網(wǎng)頁(yè)內(nèi)容。
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);
上面代碼通過(guò)request.url屬性,判斷請(qǐng)求的網(wǎng)址,從而返回不同的內(nèi)容。注意,app.use方法一共登記了三個(gè)中間件,只要請(qǐng)求路徑匹配,就不會(huì)將執(zhí)行權(quán)交給下一個(gè)中間件。因此,最后一個(gè)中間件會(huì)返回404錯(cuò)誤,即前面的中間件都沒(méi)匹配請(qǐng)求路徑,找不到所要請(qǐng)求的資源。
除了在回調(diào)函數(shù)內(nèi)部判斷請(qǐng)求的網(wǎng)址,use方法也允許將請(qǐng)求網(wǎng)址寫(xiě)在第一個(gè)參數(shù)。這代表,只有請(qǐng)求路徑匹配這個(gè)參數(shù),后面的中間件才會(huì)生效。無(wú)疑,這樣寫(xiě)更加清晰和方便。
app.use('/path', someMiddleware);
上面代碼表示,只對(duì)根目錄的請(qǐng)求,調(diào)用某個(gè)中間件。
因此,上面的代碼可以寫(xiě)成下面的樣子。
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的方法
all方法和HTTP動(dòng)詞方法
針對(duì)不同的請(qǐng)求,Express提供了use方法的一些別名。比如,上面代碼也可以用別名的形式來(lái)寫(xiě)。
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方法表示,所有請(qǐng)求都必須通過(guò)該中間件,參數(shù)中的“*”表示對(duì)所有路徑有效。get方法則是只有GET動(dòng)詞的HTTP請(qǐng)求通過(guò)該中間件,它的第一個(gè)參數(shù)是請(qǐng)求的路徑。由于get方法的回調(diào)函數(shù)沒(méi)有調(diào)用next方法,所以只要有一個(gè)中間件被調(diào)用了,后面的中間件就不會(huì)再被調(diào)用了。
除了get方法以外,Express還提供post、put、delete方法,即HTTP動(dòng)詞都是Express的方法。
這些方法的第一個(gè)參數(shù),都是請(qǐng)求的路徑。除了絕對(duì)匹配以外,Express允許模式匹配。
app.get("/hello/:who", function(req, res) {
res.end("Hello, " + req.params.who + ".");
});
上面代碼將匹配“/hello/alice”網(wǎng)址,網(wǎng)址中的alice將被捕獲,作為req.params.who屬性的值。需要注意的是,捕獲后需要對(duì)網(wǎng)址進(jìn)行檢查,過(guò)濾不安全字符,上面的寫(xiě)法只是為了演示,生產(chǎn)中不應(yīng)這樣直接使用用戶提供的值。
如果在模式參數(shù)后面加上問(wèn)號(hào),表示該參數(shù)可選。
app.get('/hello/:who?',function(req,res) {
if(req.params.id) {
res.end("Hello, " + req.params.who + ".");
}
else {
res.send("Hello, Guest.");
}
});
下面是一些更復(fù)雜的模式匹配的例子。
app.get('/forum/:fid/thread/:tid', middleware)
// 匹配/commits/71dbb9c
// 或/commits/71dbb9c..4c084f9這樣的git格式的網(wǎng)址
app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
var from = req.params[0];
var to = req.params[1] || 'HEAD';
res.send('commit range ' + from + '..' + to);
});
set方法
set方法用于指定變量的值。
app.set("views", __dirname + "/views");
app.set("view engine", "jade");
上面代碼使用set方法,為系統(tǒng)變量“views”和“view engine”指定值。
response對(duì)象
(1)response.redirect方法
response.redirect方法允許網(wǎng)址的重定向。
response.redirect("/hello/anime");
response.redirect("http://www.");
response.redirect(301, "http://www.");
(2)response.sendFile方法
response.sendFile方法用于發(fā)送文件。
response.sendFile("/path/to/anime.mp4");
(3)response.render方法
response.render方法用于渲染網(wǎng)頁(yè)模板。
app.get("/", function(request, response) {
response.render("index", { message: "Hello World" });
});
上面代碼使用render方法,將message變量傳入index模板,渲染成HTML網(wǎng)頁(yè)。
requst對(duì)象
(1)request.ip
request.ip屬性用于獲得HTTP請(qǐng)求的IP地址。
(2)request.files
request.files用于獲取上傳的文件。
搭建HTTPs服務(wù)器
使用Express搭建HTTPs加密服務(wù)器,也很簡(jiǎn)單。
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');
本文由博客群發(fā)一文多發(fā)等運(yùn)營(yíng)工具平臺(tái) OpenWrite 發(fā)布
|