Express详解

Express详解

安装 express

在目标文件夹下执行如下命令:

cnpm i express
Express详解

安装完后,在目标文件夹下新建index.js文件:

// 引入 express 模块
var express = require('express');

// 创建 express 实例
var app = express();

// 响应HTTP的GET方法
app.get('/', function (req, res) {
 res.send('Hello World!');
});

// 监听到8000端口
app.listen(8000, function () {
 console.log('Hello World is listening at port 8000');
});
Express详解

然后在Node.js的命令行环境下执行“node index.js”命令,网站就运行起来了。浏览器访问一下,可以输出相应的信息。

使用

const express = require("express");
const app = express();

// 处理文件路径的模块
const path = require("path");

// view处理
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// 定义一个存放静态资源的目录
app.use(express.static("src"));

// 页面路由的处理,此处表示首页的时候处理index
const index = require("./routes/index");
app.use("/", index);

const server = app.listen(8080, function(){
	console.log("启动成功!");
})
Express详解

中间件

官方介绍:

http://expressjs.com/zh-cn/guide/writing-middleware.html

http://expressjs.com/zh-cn/guide/using-middleware.html

Express 是一个自身功能极简,完全是由路由和中间件构成一个的 web 开发框架:从本质上来说,一个 Express 应用就是在调用各种中间件。

中间件(Middleware) 就是处理HTTP请求的函数,它最大的特点就是,一个中间件处理完,再传递给下一个中间件。App实例在运行过程中,会调用一系列的中间件。

每个中间件可以从App实例,接收三个参数,依次为request对象(代表HTTP请求)、response对象(代表HTTP回应),next回调函数(代表下一个中间件)。每个中间件都可以对HTTP请求(request对象)进行加工,并且决定是否调用next方法,将request对象再传给下一个中间件。

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!
");
});

http.createServer(app).listen(1337);
Express详解

上面代码使用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!
");
  } 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!
");
});

http.createServer(app).listen(1337);
Express详解

上面代码通过request.url属性,判断请求的网址,从而返回不同的内容。注意,app.use方法一共登记了三个中间件,只要请求路径匹配,就不会将执行权交给下一个中间件。因此,最后一个中间件会返回404错误,即前面的中间件都没匹配请求路径,找不到所要请求的资源。

除了在回调函数内部判断请求的网址,use方法也允许将请求网址写在第一个参数。这代表,只有请求路径匹配这个参数,后面的中间件才会生效。无疑,这样写更加清晰和方便。

app.use('/path', someMiddleware);
Express详解

上面代码表示,只对根目录的请求,调用某个中间件。

因此,上面的代码可以写成下面的样子。

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!
");
});

app.use("/about", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Welcome to the about page!
");
});

app.use(function(request, response) {
  response.writeHead(404, { "Content-Type": "text/plain" });
  response.end("404 error!
");
});

http.createServer(app).listen(1337);
Express详解

当你不写路径的时候,实际上就相当于"/",就是所有网址

路由get、post这些东西,也是中间件,中间件讲究顺序,匹配上第一个之后,就不会往后匹配了,next函数才能够继续往后匹配。如下实例:

app.get("/",function(req,res,next){
    console.log("1");
    next();
});

app.get("/",function(req,res){
    console.log("2");
});
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);
Express详解

上面代码的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 + ".");
});
Express详解

上面代码将匹配“/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.");
	}
});
Express详解

set方法

set方法用于指定变量的值。

app.set("views", __dirname + "/views");

app.set("view engine", "jade");
Express详解

 上面代码使用set方法,为系统变量“views”和“view engine”指定值。

response对象

API 含义
res.app 同req.app
res.append() 追加指定HTTP头
res.set() 在res.append()后将重置之前设置的头
res.cookie() 设置Cookie
res.clearCookie() 清除Cookie
res.download() 传送指定路径的文件
res.get() 返回指定的HTTP头
res.json() 传送JSON响应
res.jsonp() 传送JSONP响应
res.location() 只设置响应的Location HTTP头,不设置状态码或者close response
res.redirect() 设置响应的Location HTTP头,并且设置状态码302
res.send() 传送HTTP响应
res.sendFile() 传送指定路径的文件 -会自动根据文件extension设定Content-Type
res.set() 设置HTTP头,传入object可以一次设置多个头
res.status() 设置HTTP状态码
res.type() 设置Content-Type的MIME类型

(1)response.redirect方法

response.redirect方法允许网址的重定向。

response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com"); 

res.redirect()默认响应状态码是302 

可以更改这个状态码作为res.redirect()的第一个参数

//app.js
var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.redirect(302, 'demo');
});
app.get('/demo', function(req, res){
  res.end();
});

app.listen(3000);
Express详解

当在url地址栏中输入http://localhost:3000

页面就会重定向到http://localhost:3000/demo

(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网页。

(4)res.send()

res.send用于向客户端响应信息 

并且它的强大之处在于可以智能的处理我们传递的不同类型参数

app.get('/', function(req, res, next){
  res.send('express');
});
Express详解

当参数为字符串,会将响应头Content-Type默认设置为text/html

也就是解析为html呈现在我们的页面上

app.get('/', function(req, res){
  res.send(200);
});
Express详解

当参数为数字,会自动帮我们设置响应体(状态码…)

app.get('/', function(req, res){
  res.send([1, 2, 3]);
});
Express详解

 当参数为数组或对象,它会响应一个JSON

requst对象

API 含义
req.app 当callback为外部文件时,用于访问express的实例
req.baseUrl 获取路由当前安装的URL路径
req.body/cookies 获得「请求主体」/ Cookies
req.fresh/stale 判断请求是否还「新鲜」
req.hostname/ip 获取主机名和IP地址
req.originalUrl 获取原始请求URL
req.params 获取路由的parameters
req.path 获取请求路径
req.protocol 获取协议类型
req.query 获取URL的查询参数串
req.route 获取当前匹配的路由
req.subdomains 获取子域名
req.acceptsCharsets 返回指定字符集的第一个可接受字符编码
req.acceptsEncodings 返回指定字符集的第一个可接受字符编码
req.acceptsLanguages 返回指定字符集的第一个可接受字符编码
req.accepts() 检查可接受的请求的文档类型
req.get() 获取指定的HTTP请求头
req.is() 判断请求头Content-Type的MIME类型

(1)request.ip

request.ip属性用于获得HTTP请求的IP地址。

(2)request.files

request.files用于获取上传的文件。

(3)req.query

req.query可以获取请求路径参数的对象

向服务器发送请求 http://localhost:3000/?user=tester&pass[a]=123&pass[b]=456

//app.js
var express = require('express');
var app = express();

app.get('/', function(req, res, next){
  console.log(req.query);
  console.log(req.query.user); //tester
  console.log(req.query.pass.a); //123
  console.log(req.query.pass.b); //456
  res.end();
});

app.listen(3000);
Express详解

(4)req.params

req.params可以解析复杂路由规则上的属性 
(req.param综合了req.query和req.param的功能,但是被移除了不要使用)

向服务器发送请求 http://localhost:3000/123456

//app.js
var express = require('express');
var app = express();

app.get('/:id', function(req, res, next){
  console.log(req.params.id); //123456
  res.end();
});

app.listen(3000);
Express详解

这样不论我在根路径后输入的是什么都会被解析为req.params.id 

静态资源

静态资源就是指我们在开发中用到的css、js、img等等 

它们需要存放到一个静态资源目录 

当浏览器发出了一个非HTML文件请求 

服务器就会从这个静态资源目录下去查找文件 

我们一般在根目录下创建一个public文件来存储 

并在public中创建stylesheets、javascripts、images等文件夹 

用来存储特定类型的资源

指定静态资源目录的方法上面已经提到了

var path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
Express详解

比如说我们的html中有这样的代码

<link href="/javascripts/jquery.js" rel="stylesheet" media="screen">
Express详解

 那么客户端运行发出请求

服务器就会在public的javascripts文件夹下找到jquery.js静态资源

模板引擎

express框架默认是ejs和jade渲染模板 

路由

路由的意思就是根据不同的路径,来指定不同的处理方法 

我们一般把不同的路由封装进不同的模块

首先在根目录下创建一个文件夹routes存储路由

现在我在routes文件夹下创建俩个路由文件index.js和users.js

修改app.js

//app.js
var express = require('express');
var path = require('path');
var app = express();

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

app.use('/', index);
app.use('/users', users);

app.listen(3000);
Express详解

这样表示http://localhost:3000/的路由交给index处理 
http://localhost:3000/users的路由交给users处理

下面简单的实现一下路由

//routes/index.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res){
  res.end('index');
});

router.get('/123', function(){
  res.end(123);
});

module.exports = router;
Express详解
//routes/users.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  res.end('users');
});

module.exports = router;
Express详解

通过express.Router()创建的router就像一个mini版的app一样

app能做的,router都能做 

只是我们把逻辑都封装到了各个路由模块中

上面代码的结果:

 

body-parser中间件

其实express在3.x版本中内置了很多中间件

但是4.x版本就将出static以外的所有中间件全部抽离出来了

所以就需要我们单独安装

对照表如下:

Express 3.0 Express 4.0
bodyParser body-parser
compress compression
cookieSession cookie-session
logger morgan
cookieParser cookie-parser
session express-session
favicon static-favicon
response-time response-time
error-handler errorhandler
method-override method-override
timeout connect-timeout
vhost vhost
csrf csurf

刚才就提到了POST请求有所不同

不同的地方就在于我们需要body-parser这个中间件来处理数据

通过req.body来获得数据

首先使用前不要忘记下载

npm install body-parser --save
Express详解
//app.js
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', function(req, res){
  res.send('<form method="POST" action="./form">
              <input type="text" name="user">
              <input type="submit">
            </form>');
});
app.post('/form', function(req, res){
  console.log(req.body);
  var user = req.body.user;
  res.send('账号: ' + user);
});

app.listen(3000);
Express详解

 下面这四个方法分别用于对body内容采取不同的处理方法

bodyParser.json(options) 处理JSON数据

bodyParser.raw(options) 处理buffer数据

bodyParser.text(options) 处理文本数据

bodyParser.urlencoded(options) 处理UTF-8编码数据

这样我首先通过get请求获取主页面

提交表单向服务器发送post请求

服务器响应结果

express-generator:使用express搭建应用结构

express模块有一个命令行工具express,可以用来生成基于express模块的应用结构(网站结构)。

express 4.x之后,express命令被独立出来放在了express-generator模块中。我们用下面的命令全局安装express这个命令行工具:

cnpm install -g express-generator
Express详解

安装完成后,在命令行环境下执行“express --version”,可以看到express的版本是4.16.1。

好了,现在我们使用express命令来创建一个默认的网站。

在命令行环境下导航到node.js这个目录下,执行下面的命令:

express FirstExpress
Express详解

然后可以看到:

仔细看上面的图哦,它告诉了我们三类非常重要的信息:

express命令创建的网站的目录结构以及创建的文件

安装依赖(进入到HelloExpress下,执行npm install)

使用npm start启动网站(express 4.x后)

按照响应的提示依次执行:

cd FirstExpress

cnpm install

npm start
Express详解

很快就可以看到下面的图:

看到上图,说明网站已正常运行。你可以在浏览器里访问http://localhost:3000,然后就可以看到这个页面:

到这里,express框架就搭建成功!

最后附上API中文地址:https://cloud.tencent.com/developer/section/1489347