Express 框架
简介
Express 是一个简介灵活的 node.js Web应用框架。使用 Express 可以快速的搭建一个完整的功能网站。
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求
- 定义了路由表用来执行不同的 HTTP 请求
- 可以通过向模版传递参数来动态渲染HTML也页面
安装 Express
# 新项目,需要初始化,生成package.json
npm init
# 安装express
sudo $ npm install express --save
看一个小实例
const express = require('express');
// 启用一个express的实例
const app = express();
// app.get 设置基础的路由 然后吐出数据
app.get('/', function(req, res) {
res.send('Hello Express');
});
// app.listen一个端口 启动一个后台服务
const server = app.listen(8082, function() {
console.log('接口已启动');
})
安装 supervisor
可以不用每次都手动关掉程序,再启动。这个会自动,只需要刷新即可。
$ npm install supervisor -g
# 运行main.js
$ supervisor main.js
请求和响应
Request 常用的属性:
- req.query: 获取url的查询参数串
- req.params: 获取路由的parameters
Response 常用的属性:
- res.redirect: 302, 重定向
- res.json: 传送JSON响应
// http://localhost:8082/?name=kk&age=16
app.get('/', function (req, res) {
// { name: 'kk', age: '16' }
console.log(req.query);
});
// http://localhost:8082/22
app.get('/:id', function (req, res) {
// 22
console.log(req.params.id);
});
// http://localhost:8082/?name=kk&age=16
app.get('/', function(req, res) {
// res.json(req.query);
res.redirect('https://www.baidu.com');
});
静态文件
Express 提供了内置的中间件 express.static 来设置静态文件,如图片、css、script等。
创建一个小实例
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
// 创建 application/x-www-form-urlencoded 编码解析
const urlencodedParser = bodyParser.urlencoded({ extended: false })
app.use(express.static('public'));
app.get('/index', function(req, res) {
res.sendFile(__dirname + '/views/' + 'index.html');
});
app.post('/index', urlencodedParser, function(req, res) {
console.log(req.body);
res.redirect('https://www.baidu.com/s?wd='+ req.body.data + '&rsv_spt=1&rsv_iqid=0xfddb3e7d00091f5e&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=2&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=247&rsv_sug4=637');
});
app.listen(8082, function() {
console.log('接口已启动');
});
<!-- views/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试页面</title>
<link rel="stylesheet" href="../css/index.css">
</head>
<body>
Hello Express
<form action="/index" method="post">
<input type="text" name="data">
<button type="submit">提交</button>
</form>
<script src="../scripts/index.js"></script>
</body>
</html>
使用中间件
中间件(Middleware)是一个函数,它可以访问请求对象,响应对象,和web应用中处于请求-响应循环流程中的中间件,一般命名为 next 的变量。
中间件的功能包括:
- 执行任何代码
- 修改请求和响应对象
- 终结请求-响应循环
- 调用堆栈中的下一个中间件
如果当前中间件没有终结请求-响应循环,就必须调用 next() 方法将控制权交给下一个中间件,不然请求就会被挂起, 会一直等待。
应用级中间件
应用级中间件绑定到 app对象 使用 app.use() 和 app.METHOD(), 其中 METHOD 是需要处理的http请求的方法。例如 get, post, put 等等。
// 应用级中间件
app.use(function(req, res, next) {
console.log('没有写路由,如同*, 表示全部都拦截');
next();
});
// 路由级中间件
app.use('/user', function(req, res, next) {
console.log('拦截user路由');
next();
});
app.get('/user/:id', function(req, res, next) {
req.data = req.params.id;
next();
}, function(req, res, next) {
console.log(req.data);
res.send('🆔为: ' + req.data);
});
路由级中间件
路由器级中间件与应用程序级中间件的工作方式相同,只不过它绑定到的实例express.Router()。
router.use() 和 router.METHOD() 函数家在路由级中间件。
const express = require('express');
const router = express.Router();
const app = express();
router.use(function(req, res, next) {
console.log('没有写路由,如同*, 表示全部都拦截');
next();
});
router.use('/user', function(req, res, next) {
console.log('拦截user路由');
next();
});
router.get('/user/:id', function(req, res, next) {
req.data = req.params.id;
next();
}, function(req, res, next) {
console.log(req.data);
res.send('🆔为: ' + req.data);
});
// 在根路径使用路由中间件,只要经过'/', 都会使用
app.use('/', router);
app.listen('8082', function() {
console.log('接口已启动');
});
错误处理中间件
错误处理中间件始终采用四个参数。您必须提供四个参数以将其标识为错误处理中间件函数。即使您不需要使用该next对象,也必须指定它以维护签名。否则,该next对象将被解释为常规中间件,并且将无法处理错误。
app.get('/user/', function (req, res, next) {
consoleeeeeee.log(123);
});
app.use(function(error, req, res, next) {
console.log(error.stack);
res.status(500).send('Something broke!');
});
再来看看
app.get('/', function(req, res, next) {
// 故意写错
res.endss('Hello Express');
})
app.use(logErrors)
app.use(clientErrorHandler)
app.use(errorHandler)
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
// 在这种情况下,错误将明确传递给下一个错误
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
function errorHandler (err, req, res, next) {
// 设置编码格式
res.writeHead(200,{'Content-Type': 'text/html;charset=utf-8'});
res.status(500);
// res.render('error', { error: err })
res.end('啊哦😯, 程序小姐姐正在修复中... ');
}
实操代码
// 500.js
'use strict';
const http = require('http');
module.exports = function (template) {
return function serverError(err, req, res, next) {
if (!res.statusCode || res.statusCode !== 200) {
res.statusCode = 500;
}
const desc = http.STATUS_CODES[res.statusCode];
res.end(desc + '\n' + err);
// next(err); 后面不再设置error handler, 不能再调用next, 否则会触发expressFinalHandler
}
}
// 404.js
'use static';
module.exports = function (template) {
return function fileNotFound (req, res, next) {
const model = {
url: req.url,
statusCode: 404
};
if (req.xhr) {
res.send(404, model);
} else {
res.status(404);
res.render(template, req.data);
}
}
}
温馨提示
错误中间件有四个参数,第一个参数为error。
捕获到了错误,就会执行。
一般都都放在最后,作为兜底,但是像登录等例外。
扩展知识点:
日志记录库:log4js、express-log
内置中间件
从版本4.x开始,Express 不再依赖 Connect。以前包含的中间件功能现在位于单独的模块,详情查看中间件功能列表
目前 Express 具有以下内置的中间件功能:
- express.static 不多介绍了,例子上面有
- express.json 使用JSON负载解析传入的请求 v 4.16.0+ 可用
- express.urlencoded 使用URL编码的有效内容解析传入的请求 v 4.16.0+ 可用
// 跟bodyParser.urlencoded 用法一致
app.use(express.urlencoded({ extended: false }));
app.post('/index', function (req, res) {
console.log(req.body);
});
第三方中间件
使用第三方中间件向Express应用程序添加功能。
安装Node.js模块以获得所需的功能,然后在应用程序级别或路由器级别将其加载到您的应用程序中。
以下示例说明了如何安装和加载cookie解析中间件功能cookie-parser。
$ npm install cookie-parser -S
const cookieParser = require('cookie-parser');
app.use(cookieParser());
app.get('/user/', function (req, res, next) {
console.log('🍊cookie: ', req.cookies);
res.send(req.cookies['NX-ANTI-CSRF-TOKEN']);
});
更多第三方中间件,查阅列表
路由
路由是指如何定义应用的端口以及如何响应客户端的请求。
路由是由一个URL、HTTP请求(get、post 等)和若干个句柄组成。
app.METHOD(path, [callback...,]callback)
app.all() 是一个特殊的路由方法,不管是get、post、put 都支持。
// 正则匹配路径
app.get(/te(.+)t$/, function(req, res) {
res.send('123');
});
// 多个回调,用next连接
const cb1 = function(req, res, next) {
console.log('cb1');
next();
}
const cb2 = function(req, res, next) {
console.log('cb2');
next();
}
app.all('/index', [cb1, cb2], function(req, res, next) {
console.log('response will be sent by the next function...');
next;
}, function(req, res) {
res.send('end');
});
app.router()
app.router() 创建路由路径的链式路由句柄。由于路由在一个地方指定,这样做有利于创建模块坏的路由,而且减少代码冗余和拼写错误。
app.get('/index', function(req, res) {
res.send('Hello Express')
});
app.post('/index', function(req, res) {
res.send('Hello Express')
});
app.put('/index', function(req, res) {
res.send('Hello Express')
});
app.delete('/index', function(req, res) {
res.send('Hello Express')
});
// 上面那样写特别冗余,而且不好看,可以写成以下方式
app.route('/index')
.get (function(req, res) {
res.send('Hello Express')
})
.post (function(req, res) {
res.send('Hello Express')
})
.put (function(req, res) {
res.send('Hello Express')
})
.delete (function(req, res) {
res.send('Hello Express')
});
也可以直接用中间件 express.router();
使用模版引擎
需要在应用中进行如下设置才能让 Express 渲染模版文件:
- views, 放模版文件的目录,比如:app.set('views', './views');
- view engine, 引擎模版,比如: app.set('view engine', 'html');
推荐使用 swig 模版引擎,不推荐使用 jade
# 安装模版引擎
$ npm install swig --S
模版:直接拿到数据,可以直接渲染到页面上,不需要发送ajax请求。
// app.js
const express = require('express');
const swig = require('swig');
const app = express();
app.set('view cache', false);
app.set('view engine', 'html');
app.engine('html', swig.renderFile);
app.use(express.static('public'));
app.get('/', function (req, res) {
res.render('index', {
title: '测试首页',
data: '这里是插入进来的内容呢😄'
})
});
app.listen(8082, function() {
console.log('接口已启动');
});
<!-- layout.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title>
{% block head %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
<!-- index.html -->
{% extends "./layout.html" %}
{% block title %}
index {{ title }}
{% endblock %}
{% block head %}
<link rel="stylesheet" type="text/css" href="css/index.css">
{% endblock %}
{% block content %}
<div>
<h1>Hello Swig!</h1>
<span>{{ data }}</span>
</div>
{% endblock %}