MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Node.js处理HTTP请求响应详解

2023-12-065.9k 阅读

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}`);
});

在这段代码中:

  1. 首先,我们通过 require('http') 引入了 Node.js 的 http 模块。
  2. 然后使用 http.createServer 方法创建一个服务器实例。这个方法接受一个回调函数,该回调函数会在每次有新的 HTTP 请求到达时被调用。回调函数有两个参数,reqhttp.IncomingMessage 实例)代表请求,reshttp.ServerResponse 实例)代表响应。
  3. 在回调函数中,我们设置了响应的状态码为 200(表示成功),并设置了 Content - Type 头为 text/plain,这意味着响应内容是纯文本。最后,使用 res.end 方法结束响应,并发送字符串 Hello, World!
  4. 最后,我们让服务器监听 3000 端口,并在控制台打印一条消息表示服务器已启动。

HTTP 请求(http.IncomingMessage

http.IncomingMessage 实例(即上述代码中的 req)提供了关于 HTTP 请求的信息和方法。

请求方法

可以通过 req.method 获取请求的方法,常见的方法有 GETPOSTPUTDELETE 等。例如:

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}`);
});

在这个示例中:

  1. 我们定义了一个变量 data 来存储接收到的请求体数据。
  2. 监听 reqdata 事件,每次接收到数据块时,将其转换为字符串并追加到 data 中。
  3. 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 - Typeapplication/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 - Typeimage/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 中:

  1. 使用 app.getapp.post 等方法来定义不同 HTTP 方法的路由处理函数。
  2. app.get 的第一个参数是路由路径,第二个参数是处理该路由的回调函数。
  3. 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');
});

在这个示例中:

  1. 使用 app.use(bodyParser.urlencoded({ extended: false })) 来处理 URL 编码格式的表单数据。
  2. 使用 app.use(bodyParser.json()) 来处理 JSON 格式的请求体数据。
  3. /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 是捕获到的错误对象,reqres 是请求和响应对象,next 是 Express 中间件机制中的下一个函数(在这里不需要使用,但为了符合中间件的签名需要保留)。

结语

通过以上内容,我们详细了解了 Node.js 如何处理 HTTP 请求响应,从原生的 http 模块到使用 Express 框架。理解这些知识对于开发高性能、可靠的 web 应用至关重要。无论是处理简单的静态页面还是复杂的 API,掌握这些技术都能让开发者更加得心应手。在实际开发中,还需要结合具体的业务需求,合理运用这些技术,并注重性能优化和错误处理,以提供更好的用户体验。