Express.js 教程展示了如何使用 Express 框架在 JavaScript 中创建简单的 Web 应用程序。
Express.js
Express.js 是一个用于 Node.js 的免费开源 Web 应用程序框架。 Express 是一个最小且灵活的 Web 应用程序框架,为 Web 和移动应用程序提供了一组强大的功能。
它是一个快速、独立且极简的 Web 框架。
Express.js 安装
我们安装 Express 框架。稍后,我们安装了其他包,包括 Lodash、sqlite3 和 Axios。
$ node -v v18.2.0
我们使用 Node 版本 18.2.0。
$ npm init -y $ npm i express
我们使用npm工具安装Express。
网址
统一资源定位器 (URL) 是对 Web 资源的引用,它指定了它在计算机网络上的位置和检索它的机制。网络资源是可以通过网络获取的任何数据,例如 HTML 文档、PDF 文件、PNG 图像、JSON 数据或纯文本。
通用 URL 具有以下形式:
scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]
[] 括号之间的部分是可选的。
Express.js 路由
路由 将 HTTP 动词(例如 GET、POST、PUT、DELETE)和处理程序函数的 URL 路径相关联。要创建路由,我们使用 Express 应用程序对象的函数。
app.get('/', (req, res) => {
});
这里我们将 GET 请求中发送的 / 路径映射到处理函数。该函数接收请求和响应对象作为参数。
要对路由进行分组并将它们分成模块,我们可以使用Routermiddleware。
Express.js 中间件
Express中间件是框架的核心。它位于请求-响应周期的中间。中间件是管道中请求对象和响应对象之间调用的一系列函数。 Express 是一个最小的框架。大多数功能都可以作为中间件功能使用。
中间件函数用于实现身份验证、CSRF 保护、日志记录或 cookie 处理等功能。它们不用于实现应用程序的业务逻辑。
use函数将指定的中间件函数挂载到指定路径。
Express.js GET 请求示例
get 函数使用指定的回调函数将 HTTP GET 请求路由到指定的路径。
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello there!'));
app.listen(3000, () => console.log('Application started on port 3000'));
应用程序处理 GET 请求并向客户端发送一条短消息。
const express = require('express');
我们包括express包。
const app = express();
Express 应用程序已创建。
app.get('/', (req, res) => res.send('Hello there!'));
使用get 函数,我们将/ 路径映射到匿名函数,匿名函数将字符串发送回客户端。
app.listen(3000, () => console.log('Application started on port 3000'));
启动时,应用程序会侦听端口 3000。
$ node main.js Application started on port 3000
我们在本地主机上启动应用程序。
$ curl localhost:3000 Hello there!
我们使用curl 命令创建一个请求。
Express.js HTTP 标头
请求对象还包括从客户端发送的请求标头。请求标头是 HTTP 标头,其中包含有关要获取的资源以及请求资源的客户端的更多信息。
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send(`The request IP is: ${req.ip}`);
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
该示例输出生成请求的客户端的 IP 地址。
$ curl localhost:3000 The request IP is: ::1
这是一个示例输出; ::1 是 IPv6 中的环回地址。
Express.js 查询参数
查询字符串是 URL 的一部分,用于为资源请求添加一些条件。它通常是一系列键/值对。它遵循路径并以 ? 字符开头。
const express = require('express');
const app = express();
app.get('/greet', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
let name = req.query.name;
let msg = `Hello ${name}`;
res.send(msg);
});
app.listen(3000, () => console.log('Application started on port 3000'));
应用程序创建消息并将其发送到客户端。它使用来自 name 查询参数的值。
app.get('/greet', (req, res) => {
我们使用指定的回调函数将 HTTP GET 请求路由到指定的路径。回调函数接收两个参数:请求对象和响应对象。请求对象表示 HTTP 请求并具有请求查询字符串、参数、正文和 HTTP 标头的属性。
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
我们设置响应内容类型和字符集。我们的输出将是明文。默认的内容类型是text/html。
let name = req.query.name;
我们从请求查询属性中获取查询参数。它是一个包含路由中每个查询字符串参数的属性的对象。
let msg = `Hello ${name}`;
构建了一条消息。
res.send(msg);
消息被发送到客户端。
$ curl -i localhost:3000/greet?name=Lucia HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/plain; charset=utf-8 Content-Length: 11 ETag: W/"b-QOTbNGLVpb1ETGDcTt/rfpJV0wI" Date: Tue, 21 Apr 2020 06:58:12 GMT Connection: keep-alive
我们使用curl命令行工具向应用程序创建 GET 请求。使用 -i 选项,我们还包括响应头。
Express.js 路径参数
值可以通过查询参数或路径参数发送到网络应用程序。路径参数在冒号/:param 之后指定。
req.params 属性是一个包含映射到指定路由参数的属性的对象。
const express = require('express');
const app = express();
app.get('/show/:name/:age/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
let name = req.params.name;
let age = req.params.age;
let msg = `${name} is ${age} years old`;
res.send(msg);
});
app.listen(3000, () => console.log('Application started on port 3000'));
应用程序向用户返回一条消息,其中包含发送的两个路径参数。
app.get('/show/:name/:age/', (req, res) => {
路径参数定义在路由路径中;参数的名称在冒号字符之后。
let name = req.params.name; let age = req.params.age;
我们从req.params 对象中检索路径参数。
$ curl localhost:3000/show/Robert/24/ Robert is 24 years old
Express.js 路径模式匹配
可以在请求路径上使用正则表达式。这样我们就可以限制通过请求路径传递的值。
const express = require('express');
const app = express();
app.get('/city/:id([0-9]{1,5})', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send(`id: ${req.params.id}`);
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
在示例中,我们将 Id 值限制为整数,具有 1 到 5 位数字。
$ curl localhost:3000/city/12345 id: 12345
这是 Id 12345 的输出。
表达 JSON
JSON 是一种轻量级数据交换格式。它易于人类阅读和机器解析和生成。 Web 应用程序经常使用和生成 JSON 数据。
res.json 函数发送一个 JSON 响应。参数可以是任何JSON类型,包括对象、数组、字符串、布尔值、数字或null。
$ npm i lodash
我们安装 Lodash 库。
const express = require('express');
const _ = require('lodash');
const app = express();
app.get('/movies', (req, res) => {
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
let movies = { 1: 'Toy story', 2: 'The Raid', 3: 'Hero',
4: 'Ip Man', 5: 'Kung Fu Panda' };
let movie = _.sample(movies);
res.json({movie});
});
app.listen(3000, () => console.log('Application started on port 3000'));
该示例从movies 对象中选取随机电影。电影以 JSON 格式返回。
res.set({ 'Content-Type': 'application/json; charset=utf-8' });
我们为 JSON 数据设置内容类型。
let movies = { 1: 'Toy story', 2: 'The Raid', 3: 'Hero',
4: 'Ip Man', 5: 'Kung Fu Panda' };
我们在 JS 对象中有几部电影。
let movie = _.sample(movies);
使用 Lodash _sample 方法,我们随机选择一部电影。
res.json({movie});
选择的电影以JSON格式发送给客户端。
$ curl localhost:3000/movies
{"movie":"Toy story"}
$ curl localhost:3000/movies
{"movie":"Kung Fu Panda"}$
$ curl localhost:3000/movies
{"movie":"Kung Fu Panda"}
Express 正文解析器
body-parser 是一个 Node 请求主体解析中间件。它在我们的处理程序之前在中间件中解析传入的请求主体。数据在req.body 属性下可用。
对于这个例子,我们需要安装axios包。
$ npm i axios
Axios 是用于浏览器和 Node.js 的基于承诺的 HTTP 客户端
const express = require("express");
const app = express();
app.use(express.json());
app.post('/info', (req, res) => {
console.log(req.body);
res.json(req.body);
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
在示例中,我们解析 JSON 主体。
app.use(express.json());
这里我们应用body parser中间件来解析请求体中的JSON数据。
res.json(req.body);
我们将解析后的数据以JSON格式返回给客户端。
const axios = require('axios');
async function makePostRequest() {
params = {
first_name: 'John',
last_name: 'Doe',
email: 'gardener'
}
let res = await axios.post('http://localhost:3000/info/', params);
console.log(`Status code: ${res.status}`);
console.log(`Status text: ${res.statusText}`);
console.log(`Request method: ${res.request.method}`);
console.log(`Path: ${res.request.path}`);
console.log(res.data);
}
makePostRequest();
使用 Axios 库,我们向 Express 应用程序发出 POST 请求。
$ node post-request.js
Status code: 200
Status text: OK
Request method: POST
Path: /info/
{ first_name: 'John', last_name: 'Doe', email: 'gardener' }
快递单
HTTP POST 方法向服务器发送数据。它通常在上传文件或提交完整的 Web 表单时使用。从表单发送的数据存储在请求的正文中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Form</title>
</head>
<body>
<form action="message" method="post">
<div>
<label>Name:</label>
<input type="text" name="name">
</div>
<div>
<label>Message</label>
<input type="text" name="message">
</div>
<button type="submit">Send</button>
</form>
</body>
</html>
我们有一个包含两个输入字段的表单:name 和message。
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.post('/message', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
let name = req.body.name;
let message = req.body.message;
let output = `${name} says: ${message}`;
res.send(output);
});
app.listen(3000, () => {
console.log('Application started on port 3000');
})
我们在应用程序中有两条路线。 / 路径将表单带给用户。该函数只是发送包含表单的静态 HTML 文件。 /message 路径处理表单并根据发布的数据构建消息。
app.use(express.urlencoded({ extended: true }));
我们应用urlencoded中间件来处理表单。中间件使用urlencoded payloads解析传入的请求。application/x-www-form-urlencoded内容类型是默认的。(扩展选项选择特定的库来解析数据。)
let name = req.body.name;
let message = req.body.message;
let output = `${name} says: ${message}`;
我们从请求正文中获取两个参数并构建输出。
res.send(output);
输出被发送到客户端。
快递发送文件
sendFile 函数在给定路径传输文件。图像显示在浏览器中。 download函数传输图像;该图像由浏览器作为附件提供。
const express = require('express');
const path = require('path');
const app = express();
app.get('/file', (req, res) => {
res.set({ 'Content-Type': 'image/jpeg' });
let file = path.join(__dirname, 'img/book.jpg');
// res.sendFile(file);
res.download(file, 'book-image.jpg');
});
app.listen(3000, () => {
console.log('Application started on port 3000');
})
该示例向客户端发送图像。请注意,由于浏览器正在执行缓存,因此我们可能看不到这两种方法之间的区别。在这种情况下,我们可以打开一个隐私窗口。
res.set({ 'Content-Type': 'image/jpeg' });
我们设置合适的内容类型。
let file = path.join(__dirname, 'img/book.jpg');
我们指定图片的路径。
表达静态文件
静态文件是不会改变的文件。它们包括 CSS 文件、JavaScript 文件和图像;也包括不包含模板指令的 HTML 文件。
为了处理静态文件,我们使用内置的static 中间件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home page</title>
</head>
<body>
<p>
This is home page
</p>
</body>
</html>
这是主页。这是一个静态 HTML 文件的示例。
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static('public'));
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
该示例显示主页的简单静态 HTML 文件。
app.use(express.static('public'));
我们使用静态中间件;静态文件存储在public 目录中。
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
sendFile 函数在给定路径传输文件。
Express 图标
网站图标,也称为网站图标,是与特定网站或网页相关联的小图标。要在 Express 应用程序中显示网站图标,我们可以使用 express-favicon 中间件。
$ npm i express-favicon
我们安装包。
$ ls public/images/ favicon.ico
我们在favicon目录中有public/images。
const express = require('express');
const path = require('path');
const favicon = require('express-favicon');
const app = express();
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico')));
app.get('/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Home page');
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
该示例显示一个图标。
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico')));
我们使用use 函数应用中间件函数。该图标位于 public/images 目录中。
表达自定义 404 错误信息
HTTP 404、404 Not Found、404、Page Not Found 错误消息是网络通信中使用的超文本传输协议 (HTTP) 标准响应代码。它表示浏览器能够与给定服务器通信,但服务器找不到请求的资源。
Express 提供基本的 404 消息。在以下示例中,我们创建了自定义的。
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Home page');
});
app.get('/about', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('About page');
});
app.get('/contact', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Contact page');
});
app.use((req, res) => {
res.statusCode = 404;
res.end("404 - page not found");
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
在示例中,我们为 404 not found 错误注册了一个错误处理函数。它显示了 404.html 文件。
app.use((req, res) => {
res.statusCode = 404;
res.end("404 - page not found");
});
我们设置状态码并完成响应过程。错误处理的映射在所有其他映射之后。
Express Router 中间件
基本路由使用应用程序对象的路由方法执行,例如get、post、put、head或delete。
可以使用Routermiddleware 将路由分组并分成模块。
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Home page');
});
router.get('/about', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('About page');
});
router.get('/contact', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Contact page');
});
module.exports = router;
routes.js模块中有3条路由,路由都绑定到Router中间件,中间件对外暴露。
const express = require('express');
const routes = require('./routes');
const app = express();
app.use(routes);
app.listen(3000, () => {
console.log('Application started on port 3000');
});
我们包含路由并使用use 将它们应用于应用程序。
Express – 使用模板引擎
模板引擎或模板处理器是一个库,旨在将模板与数据模型结合起来以生成文档。模板引擎用于生成大量电子邮件、源代码预处理或生成动态 HTML 页面。
Express 可以使用许多模板引擎;例如 Liquid、Nunjucks、Pug 或 EJS。
Liquid 是一个 JavaScript 模板引擎,由 Shopify 创建。 Liquidfiles 的扩展名为.liquid;它们是 HTML 和 Liquid 结构等静态数据的混合体。在 Liquid.js 教程中了解有关 Liquid 的更多信息。
$ npm i liquidjs
我们安装了 Liquid 模板引擎。
在 Liquid 中,我们使用双大括号分隔符 {{ }} 表示输出,使用大括号百分比分隔符 {% %} 表示逻辑。
{% if user != null %}
Hello {{ user.name }}
{% endif %}
此代码是示例 Liquid 语法。
const express = require('express');
const path = require('path');
const { Liquid } = require('liquidjs');
const app = express();
const engine = new Liquid();
app.engine('liquid', engine.express());
app.set('views', path.resolve(__dirname, 'views'));
app.set('view engine', 'liquid');
app.get('/today', (req, res) => {
let today = new Date();
res.render('show_date', {now: today});
});
app.use((req, res) => {
res.statusCode = 404;
res.end("404 - page not found");
});
app.listen(3000, () => {
console.log('Application started on port 3000');
});
在示例中,我们从路径参数中读取一个值,并将其发送到show_date.liquid模板文件进行处理。
app.engine('liquid', engine.express());
app.set('views', path.resolve(__dirname, 'views'));
app.set('view engine', 'liquid');
我们设置了 Liquid 模板引擎。模板文件位于 views 目录中。
res.render('show_date', {now: today});
render 函数呈现一个视图并将呈现的 HTML 字符串发送到客户端。第一个参数是视图名称(不带扩展名);第二个参数是locals 对象,其属性定义了视图的局部变量。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Show date</title>
</head>
<body>
<p>
Today is {{ now }}
</p>
</body>
</html>
这是show_date.liquid 模板文件。该模板由静态数据和动态数据组成。
<p>
Today is {{ now }}
</p>
使用{{}}语法,我们输出传递给模板的now变量的值。
$ curl localhost:3000/today
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Show date</title>
</head>
<body>
<p>
Today is Tue Jun 28 2022 11:15:15 GMT+0200 (Central European Summer Time)
</p>
</body>
</html>
Express SQLite 示例
在下面的示例中,我们从 SQLite 数据库发送数据。SQLite 是一个基于文件的关系数据库引擎。
$ npm install sqlite3
我们使用sqlite3包。
app-sqlite.js data cities.sql test.db
这是项目结构。
BEGIN TRANSACTION;
DROP TABLE IF EXISTS cities;
CREATE TABLE cities(id INTEGER PRIMARY KEY, name TEXT, population INTEGER);
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
COMMIT;
我们使用这些数据。
$ cd data $ sqlite3 test.db SQLite version 3.37.2 2022-01-06 13:25:41 Enter ".help" for usage hints. sqlite> .read cities.sql sqlite> select * from cities; 1|Bratislava|432000 2|Budapest|1759000 3|Prague|1280000 4|Warsaw|1748000 5|Los Angeles|3971000 6|New York|8550000 7|Edinburgh|464000 8|Berlin|3671000
我们将数据加载到test.db 数据库中。
const express = require('express');
const sqlite3 = require('sqlite3').verbose();
const app = express();
const db = new sqlite3.Database('data/test.db');
app.get('/', (req, res) => {
res.set({ 'Content-Type': 'text/plain; charset=utf-8' });
res.send('Home page');
});
app.get('/cities', (req, res) => {
const sql = 'select * from cities';
const params = [];
db.all(sql, params, (err, rows) => {
if (err) {
res.status(400).json({'error': err.message});
return;
}
if (!rows) {
res.status(204).json({'error': 'No cities found'});
return;
}
res.json({
'message':'success',
'data':rows
});
});
});
app.get('/city/:id', (req, res) => {
const sql = 'select * from cities where id = ?';
const params = [req.params.id];
db.get(sql, params, (err, row) => {
if (err) {
res.status(400).json({'error':err.message});
return;
}
if (!row) {
res.status(204).json({'error': 'City not found'});
return;
}
res.json({
'message':'success',
'data':row
});
});
});
const server = app.listen(3000, () => {
console.log('Application started on port 3000');
});
process.on('SIGINT', () => {
db.close((err) => {
console.log('Application terminating');
if (err) {
console.error(err.message);
}
console.log('Closing the database connection.');
});
server.close();
});
应用中有3个路由。一个用于主页,另一个用于所有城市,第三个用于特定城市。城市以 JSON 格式返回。
const sqlite3 = require('sqlite3').verbose();
我们包含sqlite3 包。 verbose 函数生成调试信息。
const db = new sqlite3.Database('data/test.db');
我们连接到数据库文件。
app.get('/cities', (req, res) => {
const sql = 'select * from cities';
const params = [];
...
对于 /cities 路由,我们从数据库中获取所有行并将它们作为 JSON 数据发送到客户端。没有参数传递给 SQL 语句。
db.all(sql, params, (err, rows) => {
all 函数使用指定的参数运行 SQL 查询,然后使用所有结果行调用回调。
if (err) {
res.status(400).json({'error': err.message});
return;
}
如果有错误,我们发送 400 状态码并返回。
if (!rows) {
res.status(204).json({'error': 'No cities found'});
return;
}
如果没有找到数据,我们会发送 204 No content 状态码。
res.json({
'message':'success',
'data':rows
});
我们使用 json 函数发送一个 JSON 响应,其中包含选定的行。
app.get('/city/:id', (req, res) => {
const sql = 'select * from cities where id = ?';
const params = [req.params.id];
...
在这条路线中,我们搜索具有特定 Id 的城市。 params 数组包含来自请求路径参数的 Id。
db.get(sql, params, (err, row) => {
get 函数使用指定的参数运行 SQL 查询,然后使用第一个结果行调用回调。
res.json({
'message':'success',
'data':row
});
所选行以 JSON 格式发送到客户端。
$ curl localhost:3000/cities
{"message":"success","data":[{"id":1,"name":"Bratislava","population":432000},
{"id":2,"name":"Budapest","population":1759000},
{"id":3,"name":"Prague","population":1280000},
{"id":4,"name":"Warsaw","population":1748000},
{"id":5,"name":"Los Angeles","population":3971000},
{"id":6,"name":"New York","population":8550000},
{"id":7,"name":"Edinburgh","population":464000},
{"id":8,"name":"Berlin","population":3671000}]}
在本文中,我们介绍了 Express.js 网络框架。
列出所有 JavaScript 教程。
