【Node.js入门笔记9---http 模块】
Node.js入门笔记9
- Node.js---http 模块
- 一、核心功能
- 0.学习http的前提
- 1. 创建 HTTP 服务器
- 2. 处理请求和响应
- 二、进阶用法
- 1. 路由管理
- 2. 处理 POST 请求
- 3. 中间件模式
- 三、常见场景
- 四、错误处理与安全
Node.js—http 模块
Node.js 的 http 模块是构建网络应用的核心模块,用于创建 HTTP 服务器和客户端。
一、核心功能
0.学习http的前提
如果要在 JavaScript 代码中,使用 http模块来操作文件,则需要使用如下的方式先导入它:
const http = require('http');
1. 创建 HTTP 服务器
语法格式:
// 1.导入 http 模块
const http = require('http');
// 2.创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 处理请求和响应
// 设置响应头
res.writeHead(200, { 'Content-Type': 'text/plain' }); // 状态码 200,文本类型
// 发送响应数据
res.end('Hello World1');
});
// 3.监听端口
// listen 方法用于启动服务器并监听指定的端口(这里是 3000)。
// 第一个参数是要监听的端口号。
// 第二个参数是一个可选的回调函数,当服务器成功启动并开始监听端口时,这个回调函数会被调用。
// 在这个例子中,回调函数只是在控制台输出一条消息,提示服务器已经在 http://localhost:3000 上运行。
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
写完代码后,在terminal运行:node 【js文件路径】
node C:\Users\Administrator\Desktop\learning\http.js
然后在浏览器打开http://localhost:3000,如图:
2. 处理请求和响应
- 请求对象 req(request 的缩写):表示客户端的请求对象,包含了关于请求的信息,如请求方法(GET、POST 等)、请求头、请求 URL 等。
req.url: 获取请求路径(如 /api/data)。
req.method: 请求方法(GET、POST 等)。
req.headers: 请求头信息。
req.on(‘data’, callback): 监听请求数据。
req.on(‘end’, callback): 监听请求结束。
- 响应对象 res(response 的缩写):表示服务器的响应对象,用于设置响应头和发送响应数据。
res.writeHead(statusCode, headers): 设置响应头。
res.write(data): 发送响应数据。
res.end(): 结束响应。
res.statusCode: 响应状态码。
res.statusMessage: 响应状态消息。
二、进阶用法
1. 路由管理
语法格式:
// 1.导入http模块
const http = require('http');
// 2.创建http服务
const server = http.createServer((req, res) => {
// 3.获取请求路径和请求方法
// 这里运用了解构赋值的语法,从 req 对象中提取出 url(请求的路径)和 method(请求的方法)这两个属性。借助这两个属性,服务器就能够依据不同的请求路径和方法来提供不同的响应。
const { url, method } = req;
// 4.根据请求路径和请求方法返回不同的响应
// 若请求的路径是根路径 / 且请求方法为 GET,服务器会通过 res.end() 方法返回字符串 'Home Page' 作为响应。res.end() 方法的作用是结束响应并把数据发送给客户端
if (url === '/' && method === 'GET') {
// 返回首页
res.end('Home Page');
// 若请求的路径是 /api 且请求方法为 GET,服务器会创建一个包含 data 属性的对象,再使用 JSON.stringify() 方法将其转换为 JSON 字符串,最后通过 res.end() 方法返回该 JSON 字符串作为响应。
} else if (url === '/api' && method === 'GET') {
// 返回API数据
res.end(JSON.stringify({ data: 'API Response' }));
} else {
// 若请求的路径和方法不匹配上述任何一种情况,服务器会把响应状态码设置为 404(Not Found),并通过 res.end() 方法返回字符串 'Not Found' 作为响应。
res.writeHead(404);
res.end('Not Found');
}
});
2. 处理 POST 请求
通过这种方式,服务器就能够接收并处理客户端发送过来的数据。
// 这里定义了一个名为 body 的变量,并初始化为一个空数组。这个数组将用于存储从客户端发送过来的数据块(chunk)。
// 在 HTTP 请求中,数据可能会分多个部分(数据块)传输,因此需要一个数组来暂存这些数据块,后续再将它们合并成完整的数据。
let body = [];
// req 是 Node.js 中 HTTP 请求对象,代表客户端发送的请求。
// data 事件会在接收到请求数据时触发,每次触发时会传入一个数据块(chunk)。
// 这里通过监听 data 事件,将每个数据块存入 body 数组中。
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
// 'end' 是另一个事件名,表示请求数据已经全部传输完成。此时 body 数组中已经包含了完整的请求数据
// 最后,通过 Buffer.concat() 方法将 body 数组中的数据块合并成一个完整的 Buffer 对象,再通过 toString() 方法将其转换为字符串。
body = Buffer.concat(body).toString();
// 最后,通过 JSON.parse() 方法将字符串转换为对象,再通过 res.end() 方法返回一个包含接收到的数据的字符串。
const data = JSON.parse(body);
res.end(`Received: ${data}`);
});
3. 中间件模式
通过 use 函数注册中间件,在处理 HTTP 请求时依次执行这些中间件,直到响应结束或所有中间件执行完毕。
语法格式:
// 1. 定义中间件数组:创建了一个空数组 middlewares,用于存储所有注册的中间件函数。中间件是在请求处理过程中可以执行特定任务的函数,例如日志记录、身份验证等。
const middlewares = [];
// 2. 定义 use 函数:use 函数用于向 middlewares 数组中添加中间件函数。当调用 use 函数并传入一个中间件函数时,该函数会被添加到 middlewares 数组的末尾。
function use(middleware) {
middlewares.push(middleware);
}
// 3. 创建 HTTP 服务器
const server = http.createServer((req, res) => {
let idx = 0;
function next() {
if (idx < middlewares.length) {
middlewares[idx++](req, res, next);
} else {
// 默认响应
res.statusCode = 404;
res.end('Not Found');
}
}
next();
});
// 使用中间件
// 第一个中间件函数 (req, res, next) => { console.log('Middleware 1'); next(); }:当执行到这个中间件时,会在控制台输出 'Middleware 1',然后调用 next 函数,将控制权传递给下一个中间件。
use((req, res, next) => {
console.log('Middleware 1');
next();
});
// 第二个中间件函数 (req, res) => { res.end('Hello from Middleware'); }:当执行到这个中间件时,会结束响应并返回 'Hello from Middleware' 给客户端,此时不会再调用 next 函数,因为响应已经结束。
use((req, res) => {
res.end('Hello from Middleware');
});
三、常见场景
- 静态文件服务器:
创建了一个简单的静态文件服务器,它会根据客户端请求的 URL 尝试从 public 目录下读取对应的文件,并将文件内容返回给客户端。若文件不存在,则返回 404 错误信息。
// 引入 Node.js 的文件系统模块,用于处理文件和目录操作
const fs = require('fs');
// 引入 Node.js 的路径模块,用于处理和转换文件路径
const path = require('path');
// 创建一个 HTTP 服务器实例
const server = http.createServer((req, res) => {
// 构建要读取的文件的完整路径
// __dirname 是当前执行脚本所在的目录的绝对路径
// 'public' 是存储静态文件的目录名
// req.url 是客户端请求的 URL
const filePath = path.join(__dirname, 'public', req.url);
// 异步读取指定路径的文件内容
fs.readFile(filePath, (err, data) => {
// 检查读取文件过程中是否出现错误
if (err) {
// 如果出现错误,将响应的状态码设置为 404,表示未找到资源
res.writeHead(404);
// 向客户端发送错误信息
res.end('File not found');
} else {
// 如果读取成功,将响应的状态码设置为 200,表示请求成功
res.writeHead(200);
// 将读取到的文件内容发送给客户端
res.end(data);
}
});
});
- 代理服务器:
接收客户端的请求,然后将请求转发到指定的目标服务器(target-server.com),并将目标服务器的响应返回给客户端。这种代理服务器可以用于多种场景,如负载均衡、缓存、访问控制等。
// 创建一个 HTTP 代理服务器实例
// 当有客户端请求到达时,会触发传入的回调函数进行处理
const httpProxy = http.createServer((req, res) => {
// 创建一个对目标服务器的 HTTP 请求
// 第一个参数是一个配置对象,用于指定目标服务器的相关信息
// host 指定目标服务器的主机名,即要将请求转发到的服务器地址
// path 直接使用客户端请求的 URL 路径,将其原样转发给目标服务器
const proxyReq = http.request(
{ host: 'target-server.com', path: req.url },
// 当从目标服务器接收到响应时,会触发此回调函数
(proxyRes) => {
// 将目标服务器的响应流通过管道传输到客户端的响应流
// 也就是把目标服务器返回的响应内容直接发送给客户端
proxyRes.pipe(res);
}
);
// 将客户端的请求流通过管道传输到对目标服务器的请求流
// 这样就把客户端的请求内容(如请求体)转发给目标服务器
req.pipe(proxyReq);
});
- RESTful API:
在服务器接收到客户端请求时,根据请求的 URL 和请求方法进行不同的处理。如果请求的是 /api/users 且为 GET 请求,服务器会返回所有用户信息;如果请求的是 /api/users/某个用户 ID 且为 GET 请求,服务器会返回该指定用户的信息。
// 定义一个数组 users,用于存储用户信息
// 初始时数组中有一个用户对象,包含用户的 id 和 name
const users = [{ id: 1, name: 'Alice' }];
// 为服务器实例添加 request 事件监听器
// 当服务器接收到客户端的请求时,会触发该监听器中的回调函数
server.on('request', (req, res) => {
// 检查请求的 URL 是否为 /api/users 且请求方法为 GET
if (req.url === '/api/users' && req.method === 'GET') {
// 如果满足条件,将 users 数组转换为 JSON 字符串
// 并将其作为响应内容返回给客户端
res.end(JSON.stringify(users));
}
// 检查请求的 URL 是否以 /api/users/ 开头且请求方法为 GET
else if (req.url.startsWith('/api/users/') && req.method === 'GET') {
// 从请求的 URL 中提取用户 ID
// 先使用 split 方法按 / 分割 URL 字符串,然后取索引为 3 的元素
// 再将其转换为整数类型
const userId = parseInt(req.url.split('/')[3]);
// 在 users 数组中查找 id 等于提取的 userId 的用户对象
const user = users.find(u => u.id === userId);
// 将找到的用户对象转换为 JSON 字符串并作为响应内容返回给客户端
res.end(JSON.stringify(user));
}
});
四、错误处理与安全
- 错误处理:
这部分代码为 HTTP 服务器实例 server 添加了一个 error 事件监听器。当服务器在运行过程中遇到错误(例如端口被占用、网络连接问题等)时,会触发这个监听器,并将错误对象 err 作为参数传递给回调函数。在回调函数中,使用 console.error 将错误信息输出到控制台,以便开发者能够及时发现和排查问题。
server.on('error', (err) => {
console.error('Server error:', err);
});
- 超时设置:
server.timeout 属性用于设置服务器的超时时间,单位为毫秒。这里将超时时间设置为 5000 毫秒(即 5 秒)。当一个请求在 5 秒内没有完成处理时,服务器会自动终止该请求,并触发 timeout 事件(如果有相应的事件监听器)。这有助于防止服务器因为处理长时间运行的请求而被阻塞,从而影响其他请求的处理。
server.timeout = 5000; // 5秒
- 使用 helmet 库设置安全头:helmet 是一个 Node.js 库,它可以帮助开发者设置一系列 HTTP 响应头,以提高应用程序的安全性。例如,设置 Content-Security-Policy 头可以防止跨站脚本攻击(XSS),设置 X-Frame-Options 头可以防止点击劫持攻击等。通过使用 helmet,可以简化安全头的设置过程,并确保遵循最佳安全实践。
学习方向:安装和使用 helmet 库,了解它提供的各种安全头选项及其作用。深入学习每个安全头的具体含义和配置方法,例如 Content-Security-Policy 的指令语法和 X-Content-Type-Options 的用途。学习如何根据应用程序的具体需求和安全策略来定制 helmet 的配置。
- 验证输入数据,防止 SQL 注入或 XSS 攻击:在处理用户输入时,必须对输入数据进行验证和过滤,以防止恶意用户通过输入恶意代码来执行 SQL 注入或 XSS 攻击。例如,在处理数据库查询时,使用参数化查询可以防止 SQL 注入;在输出用户输入到 HTML 页面时,对特殊字符进行转义可以防止 XSS 攻击。
学习方向:学习 SQL 注入的原理和常见的攻击方式,以及如何使用参数化查询、预处理语句等技术来防止 SQL 注入。了解 XSS 攻击的类型(反射型、存储型、DOM 型)及其原理,学习如何对用户输入进行过滤和转义,以及使用内容安全策略来防止 XSS 攻击。探索使用验证库(如 validator.js)来简化输入数据的验证过程。
- 限制请求体大小,防止 DDoS 攻击:限制请求体的大小可以防止攻击者通过发送大量数据来耗尽服务器的资源,从而导致拒绝服务(DDoS)攻击。通过设置一个合理的请求体大小限制,服务器可以拒绝过大的请求,保护自身的稳定性和可用性。
学习方向:了解不同的 HTTP 服务器框架(如 Express.js、Koa.js 等)中如何设置请求体大小限制。
研究 DDoS 攻击的类型和原理,以及其他防止 DDoS 攻击的方法,如使用防火墙、负载均衡器等。
学习如何根据服务器的性能和应用程序的需求来确定合适的请求体大小限制。