概述
Express 是目前最流行的基于 Node.js 的 Web 开发框架,可以快速地搭建一个完整功能的网站。
Express 上手非常简单,首先新建一个项目目录,假定叫做 hello-world。
$ mkdir hello-world
进入该目录,新建一个 package.json 文件,内容如下。
{
"name": "hello-world",
"description": "hello world test app",
"version": "0.0.1",
"private": true,
"dependencies": {"express": "4.x"}
}
上面代码定义了项目的名称、描述、版本等,并且指定需要 4.0 版本以上的 Express。
然后,就可以安装了。
$ npm install
执行上面的命令以后,在项目根目录下,新建一个启动文件,假定叫做 index.js。
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.listen(8080);
然后,运行上面的启动脚本。
$ node index
现在就可以访问 http://localhost:8080
,它会在浏览器中打开当前目录的 public 子目录(严格来说,是打开 public 目录的 index.html 文件)。如果 public 目录之中有一个图片文件my_image.png
,那么可以用http://localhost:8080/my_image.png
访问该文件。
你也可以在 index.js 之中,生成动态网页。
然后,在命令行下运行启动脚本,就可以在浏览器中访问项目网站了。
$ node index
上面代码会在本机的 3000 端口启动一个网站,网页显示 Hello World。
启动脚本 index.js 的 app.get
方法,用于指定不同的访问路径所对应的回调函数,这叫做“路由”(routing)。上面代码只指定了根目录的回调函数,因此只有一个路由记录。实际应用中,可能有多个路由记录。
这时,最好就把路由放到一个单独的文件中,比如新建一个 routes 子目录。
然后,原来的 index.js 就变成下面这样。
// index.js
var express = require('express');
var app = express();
var routes = require('./routes')(app);
app.listen(3000);
运行原理
底层:http 模块
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 模块之上,加了一个中间层。
什么是中间件
简单说,中间件(middleware)就是处理 HTTP 请求的函数。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。App 实例在运行过程中,会调用一系列的中间件。
每个中间件可以从 App 实例,接收三个参数,依次为 request 对象(代表 HTTP 请求)、response 对象(代表 HTTP 回应),next 回调函数(代表下一个中间件)。每个中间件都可以对 HTTP 请求(request 对象)进行加工,并且决定是否调用 next 方法,将 request 对象再传给下一个中间件。
一个不进行任何操作、只传递 request 对象的中间件,就是下面这样。
function uselessMiddleware(req, res, next) {next();
}
上面代码的 next 就是下一个中间件。如果它带有参数,则代表抛出一个错误,参数为错误文本。
function uselessMiddleware(req, res, next) {next('出错了!');
}
抛出错误以后,后面的中间件将不再执行,直到发现一个错误处理函数为止。
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 的方法
all 方法和 HTTP 动词方法
针对不同的请求,Express 提供了 use 方法的一些别名。比如,上面代码也可以用别名的形式来写。
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 方法,所以只要有一个中间件被调用了,后面的中间件就不会再被调用了。
除了 get 方法以外,Express 还提供 post、put、delete 方法,即 HTTP 动词都是 Express 的方法。
这些方法的第一个参数,都是请求的路径。除了绝对匹配以外,Express 允许模式匹配。
app.get("/hello/:who", function(req, res) {res.end("Hello," + req.params.who + ".");
});
上面代码将匹配“/hello/alice”网址,网址中的 alice 将被捕获,作为 req.params.who 属性的值。需要注意的是,捕获后需要对网址进行检查,过滤不安全字符,上面的写法只是为了演示,生产中不应这样直接使用用户提供的值。
如果在模式参数后面加上问号,表示该参数可选。
app.get('/hello/:who?',function(req,res) {if(req.params.id) {res.end("Hello," + req.params.who + ".");
}
else {res.send("Hello, Guest.");
}
});
下面是一些更复杂的模式匹配的例子。
app.get('/forum/:fid/thread/:tid', middleware)
// 匹配 /commits/71dbb9c
// 或 /commits/71dbb9c..4c084f9 这样的 git 格式的网址
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 方法,为系统变量“views”和“view engine”指定值。
response 对象
(1)response.redirect 方法
response.redirect 方法允许网址的重定向。
response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com");
(2)response.sendFile 方法
response.sendFile 方法用于发送文件。
response.sendFile("/path/to/anime.mp4");
(3)response.render 方法
response.render 方法用于渲染网页模板。
app.get("/", function(request, response) {response.render("index", { message: "Hello World"});
});
上面代码使用 render 方法,将 message 变量传入 index 模板,渲染成 HTML 网页。
requst 对象
(1)request.ip
request.ip 属性用于获得 HTTP 请求的 IP 地址。
(2)request.files
request.files 用于获取上传的文件。
搭建 HTTPs 服务器
使用 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');
本文由博客群发一文多发等运营工具平台 OpenWrite 发布