目录
A. 全局对象
Node.js 中有一些全局对象,它们在 Node.js 程序中无需特别声明就可以直接使用。以下是一些主要的 Node.js 全局对象:
一、全局对象概述
在 Node.js 中,全局对象是在任何模块中都可以直接访问的对象。与浏览器中的 JavaScript 不同,Node.js 有自己独特的全局对象集合,这些对象提供了对各种系统资源和功能的访问。例如,global
对象是 Node.js 中最核心的全局对象之一,它类似于浏览器中的window
对象,但在 Node.js 环境中有其独特的用途和特点。
二、global
对象
-
作用和特点:
global
对象在 Node.js 中是一个顶层对象,类似于浏览器中的window
对象。它在 Node.js 应用程序的任何地方都可以访问,并且可以用来定义全局变量和函数。- 与浏览器环境不同,在 Node.js 中,顶级的变量和函数如果没有被明确地定义在某个模块的局部作用域内,就会成为
global
对象的属性。例如,如果在一个 Node.js 脚本中定义了一个变量myVar
而没有使用let
或const
等关键字进行局部变量声明,那么这个变量就会成为global.myVar
。
-
常见属性和方法:
console
:global.console
是一个内置的用于输出信息到控制台的对象。它提供了一些方法,如console.log()
用于打印一般信息,console.error()
用于打印错误信息等。例如,在调试 Node.js 应用程序时,可以使用console.log()
来输出变量的值或程序的执行状态,方便开发者了解程序的运行情况。setTimeout
和setInterval
:这些是用于在指定的时间后执行函数或定期执行函数的方法。setTimeout(func, delay)
会在延迟delay
毫秒后执行函数func
一次,而setInterval(func, delay)
会每隔delay
毫秒重复执行函数func
。例如,可以使用setInterval(() => console.log('每隔 1 秒执行一次'), 1000)
来实现每秒在控制台打印一条信息的功能。process
:global.process
是一个重要的对象,它提供了有关当前 Node.js 进程的信息和控制方法。例如,process.argv
可以获取命令行参数,这在编写可接受命令行参数的 Node.js 脚本时非常有用。例如,如果有一个脚本用于处理文件,通过process.argv
可以获取用户在命令行中指定的文件路径等参数。process.exit()
方法可以用来立即退出当前 Node.js 进程,通常用于在发生错误或满足特定条件时终止程序的执行。
三、Buffer
对象
-
作用和特点:
Buffer
是 Node.js 全局对象之一,用于处理二进制数据。在 Node.js 中,Buffer
对象用于处理原始的二进制数据,这在与底层系统交互(如文件 I/O、网络通信等)时非常重要。与字符串不同,Buffer
对象可以直接存储和操作二进制数据,而不需要进行编码和解码。- 例如,在读取和写入二进制文件时,
Buffer
对象可以用来高效地处理文件的内容,而不会出现字符编码相关的问题。在网络通信中,当接收到原始的二进制数据时,Buffer
对象可以用来存储和处理这些数据,然后根据需要进行进一步的解析或处理。
-
常见方法:
Buffer.from()
:这个方法用于创建一个新的Buffer
对象。它可以接受多种类型的参数,如字符串、数组等,并将其转换为二进制数据存储在Buffer
对象中。例如,Buffer.from('Hello', 'utf8')
会创建一个包含字符串'Hello'的 UTF-8 编码的二进制数据的Buffer
对象。Buffer.write()
:该方法用于将字符串写入Buffer
对象。它接受要写入的字符串、写入的起始位置和编码方式等参数。例如,let buffer = Buffer.alloc(10); buffer.write('World', 0, 'utf8')
会将字符串'World'写入创建的Buffer
对象中,从起始位置 0 开始,使用 UTF-8 编码。Buffer.toString()
:这个方法用于将Buffer
对象中的二进制数据转换为字符串。它接受编码方式作为参数,默认是 UTF-8 编码。例如,let buffer = Buffer.from('Hello', 'utf8'); console.log(buffer.toString())
会将Buffer
对象中的二进制数据转换回字符串并打印出来。
四、__dirname
和__filename
变量
-
作用和特点:
__dirname
是一个在每个模块中都可用的变量,它表示当前模块所在的目录的绝对路径。这在处理文件路径相关的操作时非常有用,因为它可以确保代码能够正确地定位文件和目录,而不管脚本是从哪个位置运行的。__filename
表示当前模块文件的绝对路径。它包含了文件名和完整的路径信息。与__dirname
类似,它可以帮助开发者准确地引用当前模块文件的位置,特别是在需要进行文件操作(如读取、写入、加载其他相关文件等)时。
-
示例用法:
- 假设在一个 Node.js 项目中有一个模块文件
utils.js
位于/home/user/project/utils
目录下。在utils.js
中,可以使用__dirname
和__filename
来进行文件操作。例如,如果要读取同一目录下的另一个文件data.txt
,可以使用fs.readFile(path.join(__dirname, 'data.txt'), 'utf8', callback)
,这里path.join(__dirname, 'data.txt')
会正确地构建出data.txt
文件的绝对路径,确保文件能够被正确读取,而不管项目是在哪个目录下启动的。同样,__filename
可以用于在日志记录等场景中准确记录当前模块文件的路径信息,方便在出现问题时进行调试和定位。
- 假设在一个 Node.js 项目中有一个模块文件
Node.js 的这些全局对象和变量提供了对各种系统资源和功能的便捷访问,是开发 Node.js 应用程序时经常会用到的重要工具。了解它们的特点和用法对于高效地编写 Node.js 代码非常重要。
B. 常用工具 util
在 Node.js 中,util
是一个核心模块,它提供了一些实用的函数和工具,用于处理各种常见的编程任务。以下是关于util
模块的一些主要内容:
一、功能概述
util
模块的目的是提供一些通用的工具函数,帮助开发者更方便地进行一些常见的操作,同时也提供了一些用于处理不同数据类型和对象之间关系的功能。它涵盖了多个方面的功能,从调试和错误处理到对象的格式化和类型检查等。
二、具体工具函数和方法
-
util.debuglog()
:- 作用:这个函数用于创建一个仅在特定条件下输出调试信息的函数。它可以帮助开发者在开发过程中进行有针对性的调试,而不会在生产环境中产生过多的不必要的输出。
- 用法:首先,你需要定义一个调试名称,例如
const debug = util.debuglog('myapp')
。然后,在代码中,当你想要输出调试信息时,可以调用debug('Some debug message')
。只有当NODE_DEBUG
环境变量设置为包含myapp
的字符串时(例如NODE_DEBUG=myapp node app.js
),这些调试信息才会在控制台输出。
-
util.inspect()
:- 作用:这个函数用于以易于阅读的格式返回一个对象的字符串表示形式。它对于调试和日志记录非常有用,因为它可以让开发者清楚地看到对象的结构和内容。
- 用法:例如
const obj = { a: 1, b: 2 }; console.log(util.inspect(obj))
,这将在控制台输出类似于{ a: 1, b: 2 }
的格式的对象字符串表示。你还可以通过传递一些参数来定制输出的格式,例如console.log(util.inspect(obj, { showHidden: false, depth: null, colors: true }))
,这里showHidden
参数用于控制是否显示对象的非枚举属性和原型链上的属性,depth
参数用于控制对象序列化的深度,colors
参数用于控制是否使用颜色来增强输出的可读性(如果控制台支持彩色输出)。
-
util.promisify()
:- 作用:这个函数用于将基于回调函数的异步函数转换为返回 Promise 的函数。在 Node.js 中,很多原生的 API 和一些第三方库仍然使用回调函数来处理异步操作,而使用 Promise 可以使异步代码更加简洁和可读,特别是在使用
async/await
语法时。 - 用法:例如,Node.js 的
fs.readFile()
函数是基于回调函数的异步操作。可以使用const util = require('util'); const fs = require('fs'); const readFileAsync = util.promisify(fs.readFile);
,然后就可以像这样使用readFileAsync('file.txt', 'utf8').then(data => console.log(data)).catch(error => console.error(error))
,或者使用async/await
语法async function readFileContent() { try { const data = await readFileAsync('file.txt', 'utf8'); console.log(data); } catch (error) { console.error(error); } }
。
- 作用:这个函数用于将基于回调函数的异步函数转换为返回 Promise 的函数。在 Node.js 中,很多原生的 API 和一些第三方库仍然使用回调函数来处理异步操作,而使用 Promise 可以使异步代码更加简洁和可读,特别是在使用
-
util.isArray()
、util.isBuffer()
、util.isDate()
等类型检查函数:- 作用:这些函数用于检查一个值是否属于特定的数据类型。在处理不同类型的数据时,准确地判断数据类型是很重要的,特别是在进行数据处理、错误处理或与其他函数交互时。
- 用法:例如
const value = [1, 2, 3]; console.log(util.isArray(value))
会返回true
,因为value
是一个数组。const buffer = Buffer.from('Hello'); console.log(util.isBuffer(buffer))
会返回true
,因为buffer
是一个Buffer
对象。const date = new Date(); console.log(util.isDate(date))
会返回true
,因为date
是一个Date
对象。这些函数可以帮助开发者在代码中准确地处理不同类型的数据,避免类型错误导致的问题。
三、在开发中的作用和意义
-
提高代码的可读性和可维护性:
- 通过
util.inspect()
函数可以清晰地查看对象的结构和内容,在调试和日志记录时,开发者可以更直观地了解程序的内部状态,而不需要手动编写复杂的代码来遍历和打印对象的属性。这使得代码的调试过程更加高效,减少了因为不清楚对象内部状态而导致的错误排查时间。 - 使用
util.promisify()
将基于回调的异步函数转换为 Promise 形式,使得异步代码的结构更加清晰和易于理解。与传统的回调函数嵌套相比,Promise 形式的异步代码更符合线性的思维方式,代码的可读性大大提高。在大型项目中,这有助于团队成员更好地理解和维护异步操作相关的代码。
- 通过
-
简化常见任务的处理:
util.debuglog()
函数使得调试信息的输出可以根据特定的条件进行控制。在开发过程中,开发者可以有针对性地输出调试信息,而在生产环境中可以避免输出过多的调试信息,从而提高程序的性能和安全性。例如,在一个复杂的服务器应用程序中,可以使用util.debuglog()
来输出关于请求处理、数据库操作等方面的调试信息,在开发阶段帮助开发者快速定位问题,而在生产环境中通过不设置相应的环境变量来关闭这些调试输出。- 类型检查函数(如
util.isArray()
等)可以在代码中确保数据的类型符合预期。在处理多种类型的数据时,通过这些函数进行类型检查,可以避免因为类型不匹配而导致的错误。例如,在一个函数中,如果期望接收一个数组作为参数,如果不进行类型检查,当传入其他类型的数据时可能会导致程序出现错误。使用util.isArray()
进行类型检查后,可以在函数内部针对数组类型进行正确的操作,提高代码的健壮性。
总之,util
模块提供了一系列实用的工具函数,帮助开发者更高效地进行 Node.js 应用程序的开发、调试和维护。熟练掌握和使用这些工具函数可以提高代码的质量和开发效率。
C. 事件驱动 events
在 Node.js 中,events
模块是实现事件驱动编程的核心模块之一。
一、事件驱动编程概述
事件驱动编程是一种编程范式,其中程序的执行流程由各种事件的发生来驱动。在 Node.js 中,事件驱动编程的概念尤为重要,因为它的异步非阻塞特性使得事件的处理成为处理并发操作和 I/O 操作的关键方式。例如,当一个网络请求到达服务器时,这会触发一个“请求接收”事件;当文件读取完成时,会触发一个“文件读取完成”事件。通过对这些事件的监听和处理,程序可以高效地响应各种不同的情况,而不需要采用传统的线性等待的方式。
二、Node.js 的events
模块
-
核心概念和基本用法:
events
模块提供了一个简单的事件发射器(Event Emitter)模式的实现。事件发射器是一个对象,它可以触发事件并让其他对象监听这些事件并作出响应。- 首先,需要创建一个事件发射器对象。可以通过
const EventEmitter = require('events'); const myEmitter = new EventEmitter();
来创建。然后,可以使用on
方法来监听事件。例如,myEmitter.on('eventName', callbackFunction)
,这里eventName
是事件的名称,callbackFunction
是当事件触发时要执行的回调函数。当触发事件时,使用emit
方法,如myEmitter.emit('eventName')
,这会导致所有注册在eventName
事件上的回调函数被执行。
-
事件的监听和触发机制:
- 当使用
on
方法注册事件监听器时,Node.js 会将这些监听器函数存储在一个内部列表中,与对应的事件名称相关联。当emit
方法被调用并传递相应的事件名称时,Node.js 会遍历这个事件名称对应的监听器列表,并按照注册的顺序依次调用每个监听器函数。这个过程是异步的,不会阻塞程序的其他部分执行。 - 例如,在一个实时聊天应用中,当有新消息到达时,可以触发一个“新消息”事件。多个模块可以监听这个事件,比如一个模块用于将消息显示在用户界面上,另一个模块用于将消息存储到数据库中。当“新消息”事件被触发时,这些不同的模块中的监听器函数会被依次调用,实现不同的功能,而整个过程不会因为等待某个操作完成而阻塞其他用户的操作。
- 当使用
-
事件传播和冒泡:
- 在
events
模块中,事件可以进行传播和冒泡,类似于 DOM 事件中的概念。当一个事件在一个事件发射器对象上触发时,如果没有在该对象上被处理,它可以向上传播到其父级对象(如果存在)的事件监听器中。 - 例如,假设有一个父级事件发射器对象
parentEmitter
和一个子级事件发射器对象childEmitter
。如果在childEmitter
上触发一个事件,并且在childEmitter
上没有处理这个事件的监听器,那么这个事件可以传播到parentEmitter
上的监听器。这种机制可以用于构建层次化的事件处理结构,例如在一个复杂的应用程序中,不同模块之间的事件可以通过这种方式进行传递和处理,实现更灵活的功能组合。
- 在
三、在 Node.js 应用中的实际应用场景
-
处理用户交互事件:
- 在 Web 应用服务器中,当用户通过浏览器发送请求时,服务器可以将这个请求看作一个事件。通过
events
模块,可以监听“请求到达”事件,并在事件处理函数中进行请求的解析、处理和响应。例如,使用 Express 框架构建的 Web 应用,当接收到 HTTP 请求时,会触发一系列与请求处理相关的事件,开发者可以通过注册事件监听器来处理不同类型的请求、验证用户身份、从数据库获取数据并返回响应等操作,每个操作都可以作为一个独立的事件处理环节。
- 在 Web 应用服务器中,当用户通过浏览器发送请求时,服务器可以将这个请求看作一个事件。通过
-
处理文件系统事件:
- 当监视文件系统的变化时,比如文件的创建、修改或删除,可以使用
events
模块来监听相应的文件系统事件。Node.js 的fs.watch
函数可以用来监视文件或目录的变化,并触发自定义的事件。例如,可以创建一个文件监控工具,当指定目录下的文件被修改时,触发一个“文件修改”事件,然后可以执行相应的处理逻辑,如重新加载文件内容、进行文件备份等操作。
- 当监视文件系统的变化时,比如文件的创建、修改或删除,可以使用
-
处理异步操作完成事件:
- 在 Node.js 中,很多异步操作(如数据库查询、网络请求等)完成时需要进行相应的处理。通过
events
模块,可以定义一个自定义事件,在异步操作完成时触发该事件,并在事件监听器中执行后续的处理逻辑。例如,在一个数据库操作模块中,当数据库查询完成时,触发一个“数据库查询完成”事件,其他模块可以监听这个事件并根据查询结果进行进一步的处理,如数据展示、数据分析等。这种方式可以将异步操作的完成与后续的处理逻辑解耦,提高代码的可维护性和可扩展性。
- 在 Node.js 中,很多异步操作(如数据库查询、网络请求等)完成时需要进行相应的处理。通过
总之,events
模块在 Node.js 中提供了一种强大的事件驱动编程机制,使得开发者能够更灵活地处理各种不同的事件,构建高效、可扩展的应用程序。通过合理地利用事件的监听和触发机制,以及事件的传播和冒泡特性,可以实现复杂的应用程序逻辑,提高代码的复用性和可维护性。
D. 文件系统 fs
在 Node.js 中,fs
(文件系统)模块是用于与文件系统进行交互的核心模块。它提供了一系列 API 来执行文件和目录的创建、读取、写入、更新和删除等操作。
一、文件操作
- 文件读取:
fs.readFile()
是用于读取文件内容的常用方法。它接受文件路径、编码方式(默认为utf8
)和一个回调函数作为参数。回调函数有两个参数,第一个是错误对象(如果读取失败),第二个是读取到的文件内容(如果成功)。- 例如:
const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件出错:', err);
return;
}
console.log('文件内容:', data);
});
- 这种异步读取文件的方式允许程序在读取文件的同时继续执行其他任务,不会因为等待文件读取而阻塞程序的执行。当文件读取完成后,回调函数会被调用并处理结果。
- 文件写入:
fs.writeFile()
用于将数据写入文件。它接受文件路径、要写入的数据和可选的选项对象(如编码方式等)以及一个回调函数。如果文件不存在,它会自动创建文件。- 例如:
fs.writeFile('newFile.txt', '这是新写入的内容', (err) => {
if (err) {
console.error('写入文件出错:', err);
return;
}
console.log('文件写入成功');
});
- 与文件读取类似,文件写入也是异步操作,确保程序在执行写入操作时不会被长时间阻塞。
二、目录操作
- 目录创建:
fs.mkdir()
用于创建目录。它接受目录路径和一个可选的回调函数。如果目录已经存在,它会抛出一个错误。- 例如:
fs.mkdir('newDirectory', (err) => {
if (err) {
console.error('创建目录出错:', err);
return;
}
console.log('目录创建成功');
});
- 在实际应用中,可能需要根据不同的情况创建多层级的目录结构,可以结合其他方法来确保目录创建的完整性。
- 目录读取(获取目录内容):
fs.readdir()
用于读取目录的内容,返回一个包含目录中所有文件和子目录名称的数组。它接受目录路径和一个回调函数。- 例如:
fs.readdir('someDirectory', (err, files) => {
if (err) {
console.error('读取目录出错:', err);
return;
}
console.log('目录中的文件和子目录:', files);
});
- 这个方法可以用于遍历目录结构,对目录中的每个文件或子目录进行进一步的操作,比如筛选特定类型的文件、统计文件数量等。
三、文件和目录的高级操作
- 文件流(File Streams):
- Node.js 中的文件流提供了一种高效的方式来处理大文件,避免一次性将整个文件读入内存。
fs.createReadStream()
用于创建可读文件流,fs.createWriteStream()
用于创建可写文件流。 - 例如,使用可读文件流来读取一个大文件并将其内容复制到另一个文件中:
- Node.js 中的文件流提供了一种高效的方式来处理大文件,避免一次性将整个文件读入内存。
const readStream = fs.createReadStream('bigFile.txt');
const writeStream = fs.createWriteStream('copyOfBigFile.txt');
readStream.pipe(writeStream);
- 这里的
pipe
方法自动管理数据的流动,从可读流读取数据并将其写入可写流,实现高效的文件复制操作。对于处理大型日志文件、视频文件等大文件操作非常有用,可以减少内存占用并提高性能。
- 文件属性操作:
fs.stat()
可以获取文件或目录的状态信息,包括文件大小、创建时间、修改时间等。它接受文件路径和一个回调函数,回调函数的参数是一个fs.Stats
对象,包含了文件或目录的详细信息。- 例如:
fs.stat('someFile.txt', (err, stats) => {
if (err) {
console.error('获取文件状态出错:', err);
return;
}
console.log('文件大小:', stats.size);
console.log('创建时间:', stats.birthtime);
console.log('修改时间:', stats.mtime);
});
- 这些文件属性信息在很多应用场景中都很有用,比如判断文件是否过期、根据文件大小进行不同的处理等。
四、错误处理
在文件系统操作中,错误处理非常重要。fs
模块的大多数方法都遵循 Node.js 的异步回调模式,其中回调函数的第一个参数通常是错误对象(如果操作失败)。
-
常见错误类型:
- 文件不存在错误:当尝试读取或操作一个不存在的文件时,会触发相应的错误。例如,
fs.readFile('nonexistent.txt', (err, data) => {... })
会在文件不存在时在回调函数中收到一个错误对象。 - 权限错误:如果程序没有足够的权限来执行某个文件操作(如写入一个只读文件或访问受限制的目录),也会触发错误。例如,在某些操作系统中,尝试向一个没有写入权限的目录写入文件会导致权限错误。
- 文件不存在错误:当尝试读取或操作一个不存在的文件时,会触发相应的错误。例如,
-
处理错误的方式:
- 在回调函数中,应该首先检查错误对象是否存在。如果存在错误,应该适当地记录错误信息并采取相应的措施,比如向用户显示错误提示、终止相关操作或尝试其他解决方案。
- 例如:
fs.writeFile('protectedFile.txt', 'data', (err) => {
if (err) {
if (err.code === 'EACCES') {
console.error('没有权限写入文件,请检查文件权限。');
} else {
console.error('写入文件出错:', err);
}
return;
}
console.log('文件写入成功');
});
- 这种方式可以确保程序在遇到文件系统操作错误时能够正确地处理和反馈,提高程序的稳定性和可靠性。
总之,Node.js 的fs
模块提供了丰富而强大的功能来与文件系统进行交互,通过合理地使用这些功能和正确处理错误,可以构建出高效、可靠的文件操作相关的应用程序。无论是处理简单的文件读写,还是进行复杂的目录管理和文件流操作,fs
模块都是 Node.js 中不可或缺的一部分。
E. HTTP 服务器与客户端
在 Node.js 中,可以使用内置的http
模块来创建 HTTP 服务器和进行 HTTP 客户端操作。
一、HTTP 服务器
- 创建基本的 HTTP 服务器:
- 使用 Node.js 的
http
模块创建一个简单的 HTTP 服务器的基本步骤如下:
- 使用 Node.js 的
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World!');
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000/');
});
- 首先,通过
http.createServer()
方法创建一个服务器实例,这个方法接受一个回调函数作为参数。在回调函数中,req
代表请求对象,包含了客户端发送的请求信息,如请求方法、请求头、请求 URL 等;res
代表响应对象,用于向客户端发送响应数据。在这个例子中,设置了响应状态码为 200(表示成功),响应头的内容类型为text/plain
(纯文本),并向客户端发送了“Hello World!”的响应内容。最后,通过server.listen()
方法在指定的端口(这里是 3000)上启动服务器并监听客户端的连接请求。
- 处理不同的 HTTP 请求方法和路由:
- 可以根据不同的请求方法(如 GET、POST、PUT、DELETE 等)和请求路径来处理不同的业务逻辑。例如:
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
if (req.method === 'GET') {
if (pathname === '/') {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('欢迎访问首页');
} else if (pathname === '/about') {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('关于我们的页面');
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('页面未找到');
}
} else if (req.method === 'POST') {
// 处理 POST 请求的逻辑
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('POST 请求已处理');
}
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000/');
});
- 在这个例子中,使用
url.parse()
方法解析请求的 URL,然后根据不同的路径和请求方法来发送不同的响应内容。如果请求的路径不存在,就返回 404 状态码和“页面未找到”的响应内容。
- 设置响应头和状态码:
- 除了上述例子中的简单设置,还可以更灵活地设置响应头和状态码。例如,设置缓存控制、跨域访问等相关的响应头,以及根据不同的业务逻辑返回不同的状态码。
- 以下是一个设置缓存控制响应头和返回不同状态码的例子:
const http = require('http');
const server = http.createServer((req, res) => {
// 设置缓存控制响应头,让客户端缓存资源 1 小时
res.setHeader('Cache-Control', 'max-age=3600');
if (req.url === '/success') {
res.writeHead(201, {'Content-Type': 'text/plain'});
res.end('资源创建成功');
} else if (req.url === '/error') {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('服务器内部错误');
} else {
res.writeHead(400, {'Content-Type': 'text/plain'});
res.end('请求错误');
}
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000/');
});
- 在这个例子中,根据不同的请求 URL 返回不同的状态码和响应内容,同时设置了缓存控制响应头来控制客户端对资源的缓存策略。
二、HTTP 客户端
- 发送基本的 HTTP 请求:
- 在 Node.js 中,可以使用
http
模块的内置函数来发送 HTTP 请求。以下是一个使用http.get()
方法发送 GET 请求的简单例子:
- 在 Node.js 中,可以使用
const http = require('http');
http.get('http://www.example.com', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data);
});
}).on('error', (err) => {
console.error('请求出错:', err);
});
- 在这个例子中,
http.get()
方法用于发送一个 GET 请求到指定的 URL(这里是http://www.example.com
)。当接收到响应数据时,通过data
事件逐步收集数据块,当响应结束时,将收集到的数据打印到控制台。如果在请求过程中发生错误,会在error
事件中处理错误信息。
- 处理不同类型的 HTTP 请求(GET、POST 等):
- 对于不同类型的 HTTP 请求,需要使用不同的方法并设置相应的请求参数。例如,发送 POST 请求并传递数据:
const http = require('http');
const querystring = require('querystring');
const postData = querystring.stringify({
message: '这是要发送的消息'
});
const options = {
hostname: 'www.example.com',
port: 80,
path: '/submit',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('响应结束');
});
});
req.on('error', (e) => {
console.error(`请求错误: ${e.message}`);
});
// 写入请求主体
req.write(postData);
req.end();
- 在这个例子中,首先使用
querystring.stringify()
方法将数据转换为适合 POST 请求的格式。然后设置了请求的选项,包括目标主机名、端口、路径、请求方法、请求头等。创建了一个 POST 请求对象req
,并在error
事件中处理请求错误。最后,通过req.write()
方法写入请求数据,并调用req.end()
方法发送请求。
- 处理请求和响应的超时:
- 在进行 HTTP 请求时,可能会遇到请求超时的情况,比如由于网络问题或服务器响应过慢。可以通过设置超时时间来处理这种情况。以下是一个设置请求超时并处理超时错误的例子
const http = require('http');
const options = {
hostname: 'www.example.com',
port: 80,
path: '/',
method: 'GET'
};
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
res.pipe(process.stdout);
});
req.setTimeout(5000, () => {
req.abort();
console.log('请求超时,已终止');
});
req.on('error', (err) => {
if (err.code === 'ECONNRESET') {
console.log('连接被重置');
} else {
console.error('请求错误: ', err);
}
});
req.end();
- 在这个例子中,首先设置了一个 GET 请求的选项,然后创建了请求对象
req
。通过req.setTimeout()
方法设置了 5 秒的超时时间,当超时发生时,调用req.abort()
方法终止请求,并在控制台打印“请求超时,已终止”的信息。同时,在error
事件中处理了连接被重置和其他错误情况。
总之,Node.js 的http
模块提供了创建 HTTP 服务器和进行 HTTP 客户端操作的功能,通过这些功能可以构建出与 HTTP 相关的各种应用程序,无论是简单的网页服务还是复杂的网络通信应用。