前言
在 Node.js 的广阔生态中,Express 框架无疑是一颗璀璨的明星。它以简洁、灵活的特点,成为众多开发者构建 Web 应用的首选工具。今天,就让我们深入探讨 Node.js 中的 Express 框架,揭开它的神秘面纱。
一、Express 框架概述
Express 是一个基于 Node.js 平台的极简、灵活的 Web 应用开发框架,它提供了一系列强大的功能,帮助开发者快速搭建 Web 服务器和处理各种请求。与原生的 Node.js HTTP 模块相比,Express 简化了许多繁琐的操作,使代码更加简洁易读。
Express 框架的核心概念包括路由、中间件和视图引擎。路由负责处理不同的 URL 请求,并将其映射到相应的处理函数;中间件则可以在请求处理的过程中执行一些额外的操作,如日志记录、身份验证等;视图引擎则用于渲染动态页面,将数据与模板相结合,生成最终的 HTML 页面。
二、Express 框架的安装与基本使用
要使用 Express 框架,首先需要安装它。在 Node.js 项目的根目录下,通过 npm 命令即可轻松安装:
npm install express
安装完成后,就可以在项目中引入 Express 并创建一个简单的 Web 服务器了:
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
在上述代码中,我们首先引入了 Express 模块,然后创建了一个 Express 应用实例。接着,我们定义了一个根路由(/),当用户访问根路径时,会返回 "Hello, World!"。最后,我们使用 app.listen() 方法启动服务器,并监听指定的端口(这里是 3000)。
三、路由的使用
路由是 Express 框架的重要组成部分,它允许我们根据不同的 URL 请求来执行不同的操作。Express 支持多种 HTTP 方法(如 GET、POST、PUT、DELETE 等)的路由定义。您还可以使用 app.all() 来处理所有 HTTP 方法,使用 app.use()来处理 指定 middleware 作为回调函数
例如,我们可以定义一个处理 POST 请求的路由:
app.post('/users', (req, res) => {
// 处理用户注册或登录等操作
res.send('User created successfully');
});
除了基本的路由定义,Express 还支持参数化路由。通过在路由路径中使用参数,我们可以动态地处理不同的请求。例如:
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
// 根据 userId 获取用户信息
res.send(`User with id ${userId}`);
});
在上述代码中,:id 是一个参数,当用户访问 /users/1 时,req.params.id 的值将为 1,我们可以根据这个参数来获取相应的用户信息。
app.put('/user', (req, res) => {
res.send('Got a PUT request at /user')
})
app.delete('/user', (req, res) => {
res.send('Got a DELETE request at /user')
})
路由路径
在 Express 中,路由路径用于定义应用程序响应请求的 URL 模式。路由路径可以是字符串、字符串模式或正则表达式。Express 使用 path-to-regexp 来匹配路由路径;请参阅 path-to-regexp 文档,了解定义 route paths 的所有可能性。
字符串模式路径
可以使用通配符 * 来匹配任意字符序列。
const express = require('express');
const app = express();
// 匹配以 /products 开头的所有路径
app.get('/products/*', (req, res) => {
res.send('This is a product-related page.');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
这里,/products/* 可以匹配 /products/books、/products/clothes 等任何以 /products 开头的路径。
正则表达式路径
正则表达式提供了更强大的匹配能力,可以根据复杂的规则来匹配 URL。
const express = require('express');
const app = express();
// 使用正则表达式匹配以数字结尾的路径
app.get(/\d$/, (req, res) => {
res.send('The URL ends with a digit.');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在这个例子中,/\d$/ 是一个正则表达式,用于匹配以数字结尾的 URL。例如,http://localhost:3000/123 会触发该路由。
组合路径
可以将上述几种方式组合使用,以满足更复杂的路由需求。
const express = require('express');
const app = express();
// 组合路径参数和通配符
app.get('/categories/:categoryId/*', (req, res) => {
const categoryId = req.params.categoryId;
res.send(`You are in category ${categoryId} and exploring sub-pages.`);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
此代码中的路由可以匹配 /categories/5/books 这样的路径,其中 5 是 categoryId,books 是通配符匹配的部分。
通过合理使用不同类型的路由路径,你可以灵活地处理各种请求,构建出功能丰富的 Express 应用程序。
路由参数
在 Express 中,路由参数是一种用于捕获 URL 中动态部分的机制。它允许你定义路由时使用占位符,当客户端发起请求时,这些占位符会被实际的值所填充,你可以通过 req.params 对象来访问这些值。以下是关于 Express 路由参数的详细介绍:
基本使用
以下是一个简单的示例,展示如何定义和使用路由参数:
const express = require('express');
const app = express();
// 定义带有路由参数的路由
app.get('/users/:userId', (req, res) => {
// 通过 req.params 获取路由参数的值
const userId = req.params.userId;
res.send(`You are requesting information about user with ID: ${userId}`);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在上述代码中,/users/:userId 里的 :userId 就是一个路由参数。当客户端访问 http://localhost:3000/users/123 时,req.params.userId 的值会被设置为 123,并将包含该用户 ID 的消息发送给客户端。
多个路由参数
你可以在一个路由路径中定义多个路由参数,每个参数用 / 分隔:
const express = require('express');
const app = express();
// 定义包含多个路由参数的路由
app.get('/posts/:postId/comments/:commentId', (req, res) => {
const postId = req.params.postId;
const commentId = req.params.commentId;
res.send(`You are viewing comment ${commentId} of post ${postId}`);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端访问 http://localhost:3000/posts/456/comments/789 时,req.params.postId 的值为 456,req.params.commentId 的值为 789。
路由参数的命名规则
- 路由参数名必须以冒号
:开头,后面紧跟参数名称,参数名称只能包含字母、数字和下划线。 - 路由参数是区分大小写的,例如
:userId和:userID是不同的参数。
可选路由参数
在 Express 中,你可以使用正则表达式来实现可选的路由参数。例如,让 :category 参数可选:
const express = require('express');
const app = express();
// 定义包含可选路由参数的路由
app.get('/products(/:category)?', (req, res) => {
const category = req.params.category;
if (category) {
res.send(`You are viewing products in the ${category} category.`);
} else {
res.send('You are viewing all products.');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在这个例子中,(/:category)? 表示 :category 参数是可选的。客户端可以访问 http://localhost:3000/products 查看所有产品,也可以访问 http://localhost:3000/products/electronics 查看特定类别的产品。
使用路由参数进行数据查询
路由参数常用于从数据库或其他数据源中检索特定的数据。以下是一个简单的模拟示例:
const express = require('express');
const app = express();
// 模拟数据库数据
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
// 根据用户 ID 获取用户信息
app.get('/users/:userId', (req, res) => {
const userId = parseInt(req.params.userId);
const user = users.find(u => u.id === userId);
if (user) {
res.json(user);
} else {
res.status(404).send('User not found');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在这个示例中,根据客户端提供的用户 ID,从模拟的用户数据数组中查找对应的用户信息并返回给客户端。如果未找到匹配的用户,则返回 404 错误。
通过使用路由参数,你可以构建出更加灵活和动态的 Express 应用程序,根据不同的请求动态处理数据
路由路径
app.route()
可以使用 为路由路径创建可链接的路由处理程序。 由于路径是在单个位置指定的,因此创建模块化路由会很有帮助,减少冗余和拼写错误也很有帮助。有关路由的更多信息,请参阅: Router() 文档。app.route()
app.route('/book')
.get((req, res) => {
res.send('Get a random book')
})
.post((req, res) => {
res.send('Add a book')
})
.put((req, res) => {
res.send('Update the book')
})
四、中间件的应用
在 Express 中,中间件是处理请求 - 响应周期的函数,它可以访问请求对象(req)、响应对象(res)以及应用程序请求 - 响应周期中的下一个中间件函数(next)。中间件可以用于执行各种任务,如日志记录、身份验证、解析请求体等。以下是关于开发和使用 Express 中间件的详细介绍:
中间件的基本结构
中间件函数通常具有以下形式:
const middlewareFunction = (req, res, next) => {
// 中间件逻辑
// 可以对 req 和 res 进行操作
next(); // 调用 next() 将控制权传递给下一个中间件或路由处理程序
};
req:请求对象,包含了客户端请求的信息。res:响应对象,用于向客户端发送响应。next:一个函数,调用它可以将控制权传递给下一个中间件或路由处理程序。
开发和使用不同类型的中间件
1. 应用级中间件
应用级中间件绑定到 app 实例上,对所有或特定路径的请求起作用。
const express = require('express');
const app = express();
// 应用级中间件,记录所有请求的时间
const requestTime = (req, res, next) => {
req.requestTime = Date.now();
next();
};
// 使用应用级中间件
app.use(requestTime);
app.get('/', (req, res) => {
const responseText = `Request received at: ${req.requestTime}`;
res.send(responseText);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在上述代码中,requestTime 中间件记录了每个请求的时间,并将其存储在 req 对象中,然后通过 next() 函数将控制权传递给下一个中间件或路由处理程序。
2. 路由级中间件
路由级中间件绑定到 express.Router() 实例上,只对特定路由的请求起作用。
const express = require('express');
const app = express();
const router = express.Router();
// 路由级中间件,检查用户是否已登录
const isAuthenticated = (req, res, next) => {
const isLoggedIn = true; // 模拟用户已登录
if (isLoggedIn) {
next();
} else {
res.status(401).send('Unauthorized');
}
};
// 使用路由级中间件
router.get('/dashboard', isAuthenticated, (req, res) => {
res.send('Welcome to the dashboard!');
});
app.use('/user', router);
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
这里,isAuthenticated 中间件用于检查用户是否已登录,只有登录的用户才能访问 /user/dashboard 路由。
3. 错误处理中间件
错误处理中间件有四个参数(err, req, res, next),用于捕获和处理应用程序中的错误。
const express = require('express');
const app = express();
// 模拟一个会抛出错误的路由
app.get('/error', (req, res, next) => {
const error = new Error('Something went wrong');
next(error);
});
// 错误处理中间件
const errorHandler = (err, req, res, next) => {
console.error(err);
res.status(500).send('Internal Server Error');
};
// 使用错误处理中间件
app.use(errorHandler);
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当 /error 路由抛出错误时,错误会被传递给 errorHandler 中间件进行处理。
4. 第三方中间件
可以使用第三方中间件来扩展 Express 应用的功能,例如 body-parser 用于解析请求体,morgan 用于日志记录。
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const morgan = require('morgan');
// 使用 morgan 中间件进行日志记录
app.use(morgan('dev'));
// 使用 body-parser 中间件解析 JSON 请求体
app.use(bodyParser.json());
app.post('/data', (req, res) => {
const data = req.body;
res.json({ message: 'Data received', data });
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在这个例子中,morgan 中间件用于记录每个请求的详细信息,body-parser 中间件用于解析客户端发送的 JSON 请求体。
中间件的使用顺序
中间件的使用顺序非常重要,它们按照定义的顺序依次执行。例如:
const express = require('express');
const app = express();
const firstMiddleware = (req, res, next) => {
console.log('First middleware');
next();
};
const secondMiddleware = (req, res, next) => {
console.log('Second middleware');
next();
};
app.use(firstMiddleware);
app.use(secondMiddleware);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
五、响应方式
| 方法 | 描述 |
|---|---|
| res.download() | 提示下载文件。 |
| res.end() | 结束响应进程。 |
| res.json() | 发送 JSON 响应。 |
| res.jsonp() | 发送支持 JSONP 的 JSON 响应。 |
| res.redirect() | 重定向请求。 |
| res.render() | 渲染视图样板。 |
| res.send() | 发送各种类型的响应。 |
| res.sendFile() | 将文件作为八位字节流发送。 |
| res.sendStatus() | 设置响应状态代码并将其字符串表示形式作为响应正文发送。 |
1. res.download()
此方法用于提示客户端下载指定的文件。它会设置适当的响应头,让浏览器将文件视为下载项。
const express = require('express');
const app = express();
const path = require('path');
app.get('/download', (req, res) => {
const filePath = path.join(__dirname, 'example.pdf');
res.download(filePath, 'downloaded_file.pdf', (err) => {
if (err) {
console.error('Error downloading file:', err);
res.status(500).send('Error downloading file');
}
});
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在上述代码中,当客户端访问 /download 路径时,服务器会提示客户端下载 example.pdf 文件,并重命名为 downloaded_file.pdf。
2. res.end()
该方法用于结束响应进程。通常在只需要发送少量数据或不需要发送额外数据时使用。
const express = require('express');
const app = express();
app.get('/end', (req, res) => {
res.end('Response ended');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
这里,当客户端访问 /end 路径时,服务器会发送 Response ended 并结束响应。
3. res.json()
此方法用于发送 JSON 响应。它会自动设置响应的 Content-Type 头为 application/json。
const express = require('express');
const app = express();
app.get('/json', (req, res) => {
const data = {
name: 'John',
age: 30
};
res.json(data);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端访问 /json 路径时,服务器会返回一个包含用户信息的 JSON 对象。
4. res.jsonp()
res.jsonp() 方法用于发送支持 JSONP 的 JSON 响应。JSONP 是一种跨域数据交互的技术。
const express = require('express');
const app = express();
app.get('/jsonp', (req, res) => {
const data = {
message: 'This is a JSONP response'
};
res.jsonp(data);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端通过 JSONP 请求 /jsonp 路径时,服务器会返回一个支持 JSONP 的响应。
5. res.redirect()
该方法用于重定向请求到另一个 URL。可以是相对路径或绝对路径。
const express = require('express');
const app = express();
app.get('/redirect', (req, res) => {
res.redirect('/new-page');
});
app.get('/new-page', (req, res) => {
res.send('You have been redirected to this new page');
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端访问 /redirect 路径时,服务器会将其重定向到 /new-page 路径。
6. res.render()
res.render() 方法用于渲染视图模板。通常需要结合模板引擎(如 EJS、Pug 等)使用。
const express = require('express');
const app = express();
const path = require('path');
// 设置视图引擎为 EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.get('/render', (req, res) => {
const data = {
title: 'My Page',
content: 'This is the content of the page'
};
res.render('index', data);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
在上述代码中,需要在 views 目录下创建一个 index.ejs 文件,服务器会将 data 对象传递给模板进行渲染。
7. res.send()
res.send() 方法可以发送各种类型的响应,包括字符串、缓冲区、对象等。它会根据发送的数据类型自动设置响应头。
const express = require('express');
const app = express();
app.get('/send-string', (req, res) => {
res.send('This is a string response');
});
app.get('/send-object', (req, res) => {
const obj = {
key: 'value'
};
res.send(obj);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
分别访问 /send-string 和 /send-object 路径,服务器会发送不同类型的响应。
8. res.sendFile()
该方法用于将文件作为八位字节流发送给客户端。
const express = require('express');
const app = express();
const path = require('path');
app.get('/send-file', (req, res) => {
const filePath = path.join(__dirname, 'example.txt');
res.sendFile(filePath);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端访问 /send-file 路径时,服务器会发送 example.txt 文件的内容。
9. res.sendStatus()
res.sendStatus() 方法用于设置响应状态代码,并将其字符串表示形式作为响应正文发送。
const express = require('express');
const app = express();
app.get('/send-status', (req, res) => {
res.sendStatus(404);
});
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
当客户端访问 /send-status 路径时,服务器会返回 404 状态码,并将 “Not Found” 作为响应正文。
这些方法在 Express 应用中非常重要,它们能帮助你根据不同的需求向客户端发送合适的响应。
六、视图引擎的选择与使用
为了渲染动态页面,Express 支持多种视图引擎,如 EJS、Pug、Handlebars 等。我们可以根据自己的需求选择合适的视图引擎。以 EJS 为例,我们首先需要安装 ejs 模块:
npm install ejs
然后,在 Express 应用中设置 EJS 为视图引擎:
app.set('view engine', 'ejs');
接下来,我们可以创建一个 EJS 模板文件(如 index.ejs),并在路由处理函数中渲染该模板:
app.get('/', (req, res) => {
const data = {
title: 'My Express App',
message: 'Welcome to my app'
};
res.render('index', data);
});
在 index.ejs 模板文件中,我们可以使用 EJS 语法来插入动态数据:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
</head>
<body>
<h1><%= message %></h1>
</body>
</html>
通过以上步骤,我们就可以使用 EJS 视图引擎来渲染动态页面了。
七、利用 Express 托管静态文件
为了提供诸如图像、CSS 文件和 JavaScript 文件之类的静态文件,使用 Express 中的 express.static 内置中间件函数。
express.static(root, [options])
例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
app.use(express.static('public'))
现在,你就可以访问 public 目录中的所有文件了:
http://localhost:3000/images/kitten.jpg
http://localhost:3000/css/style.css
http://localhost:3000/js/app.js
http://localhost:3000/images/bg.png
http://localhost:3000/hello.html
Express 在静态目录查找文件,因此,存放静态文件的目录名不会出现在 URL 中。
如果要使用多个静态资源目录,请多次调用 express.static 中间件函数:
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件时,express.static 中间件函数会根据目录的添加顺序查找所需的文件。
app.use('/static', express.static('public'))
现在,你就可以通过带有 /static 前缀地址来访问 public 目录中的文件了。
http://localhost:3000/static/images/kitten.jpg
http://localhost:3000/static/css/style.css
http://localhost:3000/static/js/app.js
http://localhost:3000/static/images/bg.png
http://localhost:3000/static/hello.html
八、Express 框架的优势与应用场景
Express 框架的优势在于其简洁性和灵活性。它提供了丰富的功能和插件,使得开发者可以根据项目的需求快速搭建 Web 应用。同时,Express 框架的性能也非常出色,能够处理大量的并发请求。
Express 框架适用于各种类型的 Web 应用开发,包括小型的个人博客、企业网站、电子商务平台等。无论是前端开发还是后端开发,Express 都能为开发者提供高效的解决方案。
Express 框架作为 Node.js 生态中的重要组成部分,为开发者提供了一个强大、灵活的 Web 应用开发平台。通过学习和掌握 Express 框架的核心概念和使用方法,我们可以更加高效地构建出功能丰富、性能卓越的 Web 应用。在未来的开发工作中,相信 Express 框架将继续发挥重要作用,助力我们实现更多的创新和突破。
结尾
本文粗略的讲解了一下Express 的简单使用, 后面会使用express 搭建一个完整的服务作为练习