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 请求中发送的 /
路径映射到处理函数。该函数接收请求和响应对象作为参数。
要对路由进行分组并将它们分成模块,我们可以使用Router
middleware。
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
。
可以使用Router
middleware 将路由分组并分成模块。
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 教程。