Node.js处理HTTP请求响应详解
Node.js 与 HTTP 基础
在深入探讨 Node.js 处理 HTTP 请求响应之前,我们先来回顾一下 HTTP 的基本概念以及 Node.js 在网络编程中的地位。
HTTP(Hyper - Text Transfer Protocol)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网数据通信的基础,采用客户端 - 服务器模型。客户端(通常是浏览器)向服务器发送请求,服务器处理请求并返回响应。
Node.js 则是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它让 JavaScript 能够在服务器端运行。Node.js 内置了对 HTTP 协议的支持,这使得我们可以轻松地搭建 HTTP 服务器来处理请求和响应。
创建基本的 HTTP 服务器
在 Node.js 中,创建一个简单的 HTTP 服务器非常容易。我们可以使用内置的 http
模块。以下是一个简单的示例:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('Hello, World!');
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
在这段代码中:
- 首先,我们通过
require('http')
引入了 Node.js 的http
模块。 - 然后使用
http.createServer
方法创建一个服务器实例。这个方法接受一个回调函数,该回调函数会在每次有新的 HTTP 请求到达时被调用。回调函数有两个参数,req
(http.IncomingMessage
实例)代表请求,res
(http.ServerResponse
实例)代表响应。 - 在回调函数中,我们设置了响应的状态码为
200
(表示成功),并设置了Content - Type
头为text/plain
,这意味着响应内容是纯文本。最后,使用res.end
方法结束响应,并发送字符串Hello, World!
。 - 最后,我们让服务器监听
3000
端口,并在控制台打印一条消息表示服务器已启动。
HTTP 请求(http.IncomingMessage
)
http.IncomingMessage
实例(即上述代码中的 req
)提供了关于 HTTP 请求的信息和方法。
请求方法
可以通过 req.method
获取请求的方法,常见的方法有 GET
、POST
、PUT
、DELETE
等。例如:
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'GET') {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('This is a GET request');
} else if (req.method === 'POST') {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('This is a POST request');
}
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
请求头
请求头包含了关于请求的附加信息,如客户端类型、接受的数据类型等。可以通过 req.headers
获取请求头,它是一个包含所有请求头字段的对象。例如:
const http = require('http');
const server = http.createServer((req, res) => {
console.log(req.headers);
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('Headers printed in console');
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
如果使用浏览器访问这个服务器,在控制台会打印出类似这样的内容:
{
'host': 'localhost:3000',
'connection': 'keep - alive',
'cache - control':'max - age = 0',
'upgrade - insecure - requests': '1',
'user - agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'accept': 'text/html,application/xhtml + xml,application/xml;q = 0.9,image/avif,image/webp,image/apng,*/*;q = 0.8,application/signed - exchange;v = b3;q = 0.9',
'accept - encoding': 'gzip, deflate, br',
'accept - language': 'en - US,en;q = 0.9'
}
请求 URL
可以通过 req.url
获取请求的 URL。例如:
const http = require('http');
const server = http.createServer((req, res) => {
console.log(req.url);
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('URL printed in console');
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
如果访问 http://localhost:3000/about
,控制台会打印 /about
。
处理 GET 请求参数
GET 请求的参数通常包含在 URL 中,以查询字符串的形式出现,例如 http://localhost:3000?name = John&age = 30
。我们可以使用 querystring
模块(Node.js 内置)或 url
模块来解析这些参数。
使用 querystring
模块
const http = require('http');
const querystring = require('querystring');
const server = http.createServer((req, res) => {
if (req.method === 'GET' && req.url.includes('?')) {
const urlParts = req.url.split('?');
const query = urlParts[1];
const params = querystring.parse(query);
console.log(params);
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end(`Name: ${params.name}, Age: ${params.age}`);
} else {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('No parameters');
}
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
在这段代码中,我们首先检查 URL 中是否包含 ?
,如果包含,则分割 URL 并获取查询字符串部分。然后使用 querystring.parse
方法将查询字符串解析为一个对象。
使用 url
模块
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
if (req.method === 'GET') {
const parsedUrl = url.parse(req.url, true);
const query = parsedUrl.query;
console.log(query);
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end(`Name: ${query.name}, Age: ${query.age}`);
} else {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('No parameters');
}
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
这里使用 url.parse(req.url, true)
,第二个参数 true
表示直接将查询字符串解析为对象,方便获取参数。
处理 POST 请求数据
处理 POST 请求数据稍微复杂一些,因为数据是在请求体中发送的。我们需要监听 data
事件来逐步接收数据,并在 end
事件触发时处理完整的数据。
const http = require('http');
const querystring = require('querystring');
const server = http.createServer((req, res) => {
let data = '';
if (req.method === 'POST') {
req.on('data', (chunk) => {
data += chunk.toString();
});
req.on('end', () => {
const params = querystring.parse(data);
console.log(params);
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end(`Name: ${params.name}, Age: ${params.age}`);
});
} else {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('Not a POST request');
}
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
在这个示例中:
- 我们定义了一个变量
data
来存储接收到的请求体数据。 - 监听
req
的data
事件,每次接收到数据块时,将其转换为字符串并追加到data
中。 - 当
end
事件触发时,表示所有数据都已接收完毕。此时使用querystring.parse
方法解析数据,并根据解析后的参数进行响应。
HTTP 响应(http.ServerResponse
)
http.ServerResponse
实例(即上述代码中的 res
)用于向客户端发送 HTTP 响应。
设置状态码
可以通过 res.statusCode
设置响应的状态码。常见的状态码有:
200
:成功404
:未找到500
:服务器内部错误
例如:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 404;
res.setHeader('Content - Type', 'text/plain');
res.end('Not Found');
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
设置响应头
通过 res.setHeader
方法设置响应头。例如,设置 Content - Type
头来指定响应内容的类型:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content - Type', 'application/json');
const responseData = { message: 'This is a JSON response' };
res.end(JSON.stringify(responseData));
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
这里设置了 Content - Type
为 application/json
,并将一个 JSON 对象字符串化后作为响应内容发送。
发送响应内容
使用 res.end
方法发送响应内容并结束响应。除了发送字符串,还可以发送 Buffer 对象等。例如:
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content - Type', 'image/png');
const fs = require('fs');
const img = fs.readFileSync('example.png');
res.end(img);
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
这段代码读取一个 PNG 图片文件,并将其作为响应内容发送,同时设置 Content - Type
为 image/png
。
重定向响应
重定向是指服务器告诉客户端去请求另一个 URL。可以通过设置状态码为 301
(永久重定向)或 302
(临时重定向),并设置 Location
头来实现。
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 302;
res.setHeader('Location', 'http://www.example.com');
res.end();
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
当客户端收到这个响应时,会自动向 http://www.example.com
发起新的请求。
处理多个路由
在实际应用中,我们通常需要处理多个不同的路由。可以通过检查 req.url
来实现简单的路由处理。
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('Home page');
} else if (req.url === '/about') {
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('About page');
} else {
res.statusCode = 404;
res.setHeader('Content - Type', 'text/plain');
res.end('Not Found');
}
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
然而,这种方式在处理复杂路由时会变得难以维护。可以使用第三方库如 express
来更优雅地处理路由。
使用 Express 处理 HTTP 请求响应
Express 是一个流行的 Node.js web 应用框架,它简化了 HTTP 服务器的搭建和路由处理。
首先,需要安装 Express:
npm install express
以下是一个使用 Express 的简单示例:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Home page');
});
app.get('/about', (req, res) => {
res.send('About page');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
在 Express 中:
- 使用
app.get
、app.post
等方法来定义不同 HTTP 方法的路由处理函数。 app.get
的第一个参数是路由路径,第二个参数是处理该路由的回调函数。res.send
方法用于发送响应,它会自动设置合适的Content - Type
头。
Express 处理 POST 请求数据
Express 提供了中间件来处理 POST 请求数据。首先需要安装 body - parser
中间件(虽然 Express 4.16.0 之后内置了类似功能,但 body - parser
仍然广泛使用):
npm install body - parser
示例代码如下:
const express = require('express');
const bodyParser = require('body - parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/submit', (req, res) => {
console.log(req.body);
res.send('Data received');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
在这个示例中:
- 使用
app.use(bodyParser.urlencoded({ extended: false }))
来处理 URL 编码格式的表单数据。 - 使用
app.use(bodyParser.json())
来处理 JSON 格式的请求体数据。 - 在
/submit
路由的处理函数中,可以通过req.body
获取解析后的 POST 请求数据。
错误处理
在处理 HTTP 请求响应时,错误处理非常重要。在原生 Node.js 的 http
模块中,可以通过监听服务器的 error
事件来处理错误。
const http = require('http');
const server = http.createServer((req, res) => {
// 模拟一个错误
throw new Error('Something went wrong');
res.statusCode = 200;
res.setHeader('Content - Type', 'text/plain');
res.end('This should not be seen');
});
server.on('error', (err) => {
console.error('Server error:', err.message);
});
const port = 3000;
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
在 Express 中,错误处理更加方便。可以定义一个全局的错误处理中间件:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
// 模拟一个错误
throw new Error('Something went wrong');
});
app.use((err, req, res, next) => {
console.error('Server error:', err.message);
res.status(500).send('Internal Server Error');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
在 Express 的错误处理中间件中,err
是捕获到的错误对象,req
和 res
是请求和响应对象,next
是 Express 中间件机制中的下一个函数(在这里不需要使用,但为了符合中间件的签名需要保留)。
结语
通过以上内容,我们详细了解了 Node.js 如何处理 HTTP 请求响应,从原生的 http
模块到使用 Express 框架。理解这些知识对于开发高性能、可靠的 web 应用至关重要。无论是处理简单的静态页面还是复杂的 API,掌握这些技术都能让开发者更加得心应手。在实际开发中,还需要结合具体的业务需求,合理运用这些技术,并注重性能优化和错误处理,以提供更好的用户体验。