NodeJs 第十五章(HTTP)

205 阅读5分钟

引言

在 Web 开发的世界里,HTTP(Hyper - Text Transfer Protocol,超文本传输协议)是基石一般的存在。它是一种应用层协议,用于分布式、协作式和超媒体信息系统,是万维网数据通信的基础。而在 Node.js 中,HTTP 模块更是为开发者提供了强大的工具,让我们能够高效地处理 HTTP 相关的操作。

一、HTTP 基础回顾

什么是HTTP

HTTP 是基于请求 - 响应模型的无状态协议。客户端发起一个 HTTP 请求,服务器接收到请求后进行处理,并返回一个 HTTP 响应。HTTP 请求由请求行、请求头、空行和请求体组成;HTTP 响应则由状态行、响应头、空行和响应体构成。常见的 HTTP 方法有 GET、POST、PUT、DELETE 等,每种方法都有其特定的用途和语义。例如,GET 方法通常用于获取资源,POST 方法用于提交数据。

HTTP模块的使用场景

  • 创建Web服务器
  • 构建API接口
  • 处理网络请求与响应
  • 实现简单的HTTP客户端功能

HTTP模块的基本概念

请求对象 (request)

请求对象是HTTP模块中的一个核心组成部分,代表了客户端向服务器发送的请求信息。请求对象包含了请求的方法(如GET、POST)、URL、头信息(headers)以及请求体(body)等内容。

响应对象 (response)

响应对象是HTTP模块中的另一个重要组成部分,代表了服务器对客户端请求的回应。通过响应对象,可以设置响应状态码、头信息以及发送响应体。

二、Node.js 中的 HTTP 模块

Node.js 自带的 HTTP 模块是构建 HTTP 服务器和客户端的核心。

监听端口

我们需要告诉服务器在哪个端口上监听传入的连接。这可以通过调用 listen 方法完成,如下所示:

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

创建简单的 HTTP 服务器

使用 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!');
});

// 监听3000端口
const port = 3000;
server.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

在这段代码中,首先引入了 HTTP 模块,然后使用createServer方法创建了一个服务器实例。createServer方法接受一个回调函数,这个回调函数会在每次收到请求时被调用,req表示请求对象,res表示响应对象。通过设置res的状态码、响应头和响应体,最终向客户端返回了一个简单的文本信息。

路由

在 Web 开发中,路由是一个非常重要的概念。它决定了用户请求的 URL 应该由哪个处理程序来处理。在原生 Node.js 中,可以使用 http 模块来创建服务器并实现简单的路由功能。

const http = require('http');

// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
    // 获取请求的 URL 和方法
    const { url, method } = req;

    // 设置响应头
    res.setHeader('Content-Type', 'text/plain');

    // 根据不同的 URL 和方法进行路由处理
    if (method === 'GET') {
        if (url === '/') {
            res.statusCode = 200;
            res.end('Welcome to the home page!');
        } else if (url === '/about') {
            res.statusCode = 200;
            res.end('This is the about page.');
        } else {
            res.statusCode = 404;
            res.end('Page not found');
        }
    } else {
        res.statusCode = 405;
        res.end('Method not allowed');
    }
});

// 监听端口
const port = 3000;
server.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});
  1. 创建服务器:使用 http.createServer() 方法创建一个 HTTP 服务器,并传入一个回调函数作为请求处理函数。
  2. 获取请求信息:在回调函数中,通过 req.urlreq.method 获取请求的 URL 和 HTTP 方法。
  3. 路由处理:根据不同的 URL 和方法进行相应的处理,设置响应状态码和响应内容。
  4. 监听端口:使用 server.listen() 方法监听指定的端口,启动服务器。

请求对象

在处理 HTTP 请求时,Node.js 提供了多种工具和方法来解析和操作请求数据。本章将详细介绍如何使用 Node.js 处理请求对象,包括如何获取请求数据、处理不同类型的请求体以及使用中间件等。

获取请求头信息

请求头包含了客户端发送的各种元数据信息,如用户代理、接受的内容类型等。你可以通过 request.headers 来访问这些信息。

const http = require('http');
const server = http.createServer((req, res) => {
    console.log(req.headers);
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,我们创建了一个简单的 HTTP 服务器,并在控制台输出了所有的请求头信息。你可以通过访问服务器并查看控制台输出来观察不同请求头的内容。

解析请求体

HTTP 请求的主体部分通常用于发送表单数据或 JSON 数据。为了正确地处理这些数据,我们需要解析请求体。Node.js 的内置模块 http 本身并不提供解析请求体的功能,因此我们通常会使用第三方库如 body-parser 或者 multer(用于处理文件上传)。

使用 body-parser

首先,你需要安装 body-parser

npm install body-parser

然后,你可以这样使用它:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json()); // 解析JSON格式的数据
app.use(bodyParser.urlencoded({ extended: true })); // 解析URL编码的数据

app.post('/submit', (req, res) => {
    console.log(req.body); // 输出解析后的请求体
    res.send('Data received!');
});

app.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,我们使用了 Express 框架和 body-parser 中间件来简化服务器的设置。bodyParser.json()bodyParser.urlencoded() 分别用于解析 JSON 和 URL 编码的数据。

手动解析请求体

如果你不想使用第三方库,也可以手动解析请求体。这通常涉及到监听 'data''end' 事件:

const http = require('http');

const server = http.createServer((req, res) => {
    let body = [];
    req.on('data', chunk => {
        body.push(chunk);
    }).on('end', () => {
        body = Buffer.concat(body).toString();
        console.log(body); // 输出请求体
        res.end('Data received!');
    });
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,我们手动收集了所有接收到的数据片段,并在 'end' 事件触发时将它们组合成完整的请求体。

处理文件上传

文件上传通常涉及更复杂的处理逻辑。对于文件上传,我们可以使用 multer 库来简化处理过程。首先,安装 multer

npm install multer

然后,你可以这样使用它:

const express = require('express');
const multer = require('multer');

const app = express();

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, 'uploads/')
    },
    filename: function (req, file, cb) {
        cb(null, file.fieldname + '-' + Date.now())
    }
});

const upload = multer({ storage: storage });

app.post('/upload', upload.single('file'), (req, res) => {
    res.send('File uploaded successfully!');
});

app.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

在这个例子中,我们配置了 Multer 来指定文件存储的位置和文件名规则,并通过 upload.single('file') 来处理单个文件的上传。

处理不同的 HTTP 请求

实际应用中,我们需要处理不同的 URL 路径和 HTTP 方法。可以通过解析req.urlreq.method来实现。

const http = require('http');
const server = http.createServer((req, res) => {
    const url = req.url;
    const method = req.method;
    if (url === '/' && method === 'GET') {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('This is the home page');
    } else if (url === '/about' && method === 'GET') {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('This is the about page');
    } else {
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Page not found');
    }
});
const port = 3000;
server.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

这段代码根据不同的 URL 路径和 HTTP 方法,返回不同的响应内容。如果客户端请求的是根路径/且方法为 GET,返回主页信息;如果请求的是/about路径且方法为 GET,返回关于页面信息;否则返回 404 页面未找到的信息。

处理get请求

在 Node.js 中处理 GET 请求非常简单,可以使用内置的 http 模块。下面是一个简单的例子:

const http = require('http');

http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/hello') {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello, world!');
  } else {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.end('Not Found');
  }
}).listen(3000);
console.log('Server running at http://localhost:3000/');

在上面的代码中,我们创建了一个简单的 HTTP 服务器,当收到 GET 请求并且 URL 是 '/hello' 时,返回 'Hello, world!',否则返回 'Not Found'。

处理 POST 请求

处理 POST 请求比处理 GET 请求稍微复杂一些,因为 POST 请求通常会传递数据。我们可以使用第三方模块 body-parser 来解析 POST 请求中的数据。下面是一个简单的例子:

const http = require('http');
const bodyParser = require('body-parser');
http.createServer((req, res) => {
  if (req.method === 'POST' && req.url === '/login') {
    let body = '';
    req.on('data', (chunk) => {
      body += chunk;
    });
    req.on('end', () => {
      let data = JSON.parse(body);
      console.log(data);
      res.writeHead(200, {'Content-Type': 'application/json'});
      res.end(JSON.stringify({message: 'Login successful'}));
    });
  } else {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.end('Not Found');
  }
}).listen(3000);

console.log('Server running at http://localhost:3000/');

在上面的代码中,我们创建了一个 HTTP 服务器,当收到 POST 请求并且 URL 是 '/login' 时,解析请求中的数据,并返回一个 JSON 响应。

处理 GET 和 POST 请求是 Node.js 开发中的基础操作,掌握这些知识将有助于你更好地开发 Web 应用程序。

处理put请求
const http = require('http');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
    if (req.method === 'PUT') {
        let body = '';
        // 监听 data 事件,接收请求体数据
        req.on('data', (chunk) => {
            body += chunk.toString();
        });

        // 监听 end 事件,当请求体数据接收完毕时触发
        req.on('end', () => {
            try {
                // 解析请求体数据
                const parsedData = querystring.parse(body);
                res.writeHead(200, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({
                    message: 'PUT request received',
                    data: parsedData
                }));
            } catch (error) {
                res.writeHead(400, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({ error: 'Invalid request body' }));
            }
        });
    } else {
        res.writeHead(405, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Method Not Allowed' }));
    }
});

const port = 3000;
server.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});
处理delete请求

HTTP 客户端请求

Node.js 的 HTTP 模块不仅可以创建服务器,还能发起 HTTP 客户端请求。

const http = require('http');
const options = {
    hostname: 'localhost',
    port: 3000,
    path: '/',
    method: 'GET'
};
const req = http.request(options, (res) => {
    let data = '';
    res.on('data', (chunk) => {
        data += chunk;
    });
    res.on('end', () => {
        console.log(data);
    });
});
req.end();

在这个例子中,使用http.request方法发起了一个 GET 请求,设置了请求的目标主机、端口、路径和方法。通过监听res的data事件和end事件,获取并处理服务器返回的数据。

响应对象

响应对象是 Node.js 中 http 模块的核心组成部分之一。它用于处理服务器发送给客户端的数据和信息。理解如何使用响应对象对于构建高性能的 Web 应用程序至关重要。

创建响应对象

响应对象通常通过 http.createServer() 方法创建。当一个请求到达服务器时,这个方法会触发一个回调函数,在该函数内部,你可以访问到一个代表请求的 request 对象以及一个代表响应的 response 对象。

const http = require('http');

http.createServer((req, res) => {
    // 在这里操作 res 对象
}).listen(3000);

设置响应状态码

响应状态码告诉客户端请求的结果。最常见的是 200 表示成功,404 表示未找到资源,500 表示服务器内部错误等。你可以使用 res.statusCode 来设置状态码。

res.statusCode = 200;

设置响应头

响应头包含有关响应的信息,如内容类型、缓存策略等。使用 res.setHeader(key, value) 方法来添加或修改响应头。

res.setHeader('Content-Type', 'text/html');

或者一次性添加多个响应头:

res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Cache-Control': 'no-cache'
});

发送数据

响应对象提供了多种方法来向客户端发送数据。res.end() 是最基本的发送方法,它既可以发送字符串也可以发送 Buffer 对象。

res.end('<html><body>你好,世界</body></html>');

如果需要分多次发送数据,可以使用 res.write() 方法,最后调用 res.end() 结束响应。

res.write('<html><head><title>我的网页</title></head>');
res.write('<body>欢迎来到我的网页</body></html>');
res.end();

处理流数据

对于大文件或实时数据流,使用流可以更高效地处理响应。你可以通过管道将读取流直接连接到响应对象上。

const fs = require('fs');

fs.createReadStream('example.txt').pipe(res);

这样可以避免内存溢出,并且能够实现高效的数据传输。

设置编码

虽然默认情况下响应内容可能是 UTF-8 编码,但有时你需要明确指定编码方式。

res.setHeader('Content-Type', 'text/plain; charset=utf-8');

错误处理

在处理响应时,错误处理是一个不可忽视的部分。确保你的应用能优雅地处理各种异常情况,比如网络问题或文件不存在的情况。

try {
    // 一些可能抛出错误的操作
} catch (error) {
    console.error(error);
    res.statusCode = 500;
    res.end('服务器发生错误');
}

处理静态资源

在 Web 开发中,处理静态文件是一个非常重要的环节。这些文件通常包括 HTML、CSS、JavaScript 文件、图片以及字体等资源。Node.js 本身并不直接支持静态文件的处理,但是通过一些流行的库和中间件,我们可以很容易地让 Node.js 应用程序处理静态文件。

在 Node.js 中使用原生模块处理静态资源,通常会用到 http 模块来创建 HTTP 服务器,fs 模块来读取文件,path 模块来处理文件路径。以下是一个完整的示例,展示如何使用 Node.js 原生模块处理静态资源:

const http = require('http');
const fs = require('fs');
const path = require('path');

// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
    // 获取请求的路径
    let filePath = '.' + req.url;
    if (filePath === './') {
        filePath = './index.html';
    }

    // 获取文件的扩展名
    const extname = String(path.extname(filePath)).toLowerCase();
    const contentTypeMap = {
        '.html': 'text/html',
        '.js': 'text/javascript',
        '.css': 'text/css',
        '.json': 'application/json',
        '.png': 'image/png',
        '.jpg': 'image/jpeg',
        '.gif': 'image/gif',
        '.svg': 'image/svg+xml'
    };

    const contentType = contentTypeMap[extname] || 'application/octet-stream';

    // 读取文件
    fs.readFile(filePath, (error, content) => {
        if (error) {
            if (error.code === 'ENOENT') {
                // 文件不存在,返回 404 错误
                fs.readFile('./404.html', (err, notFoundContent) => {
                    if (err) {
                        res.writeHead(500);
                        res.end('Internal Server Error');
                    } else {
                        res.writeHead(404, { 'Content-Type': 'text/html' });
                        res.end(notFoundContent, 'utf-8');
                    }
                });
            } else {
                // 其他错误,返回 500 错误
                res.writeHead(500);
                res.end('Internal Server Error');
            }
        } else {
            // 文件读取成功,返回文件内容
            res.writeHead(200, { 'Content-Type': contentType });
            res.end(content, 'utf-8');
        }
    });
});

const port = 3000;
server.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});

代码解释:

  1. 引入必要的模块

    • http 模块用于创建 HTTP 服务器。
    • fs 模块用于读取文件。
    • path 模块用于处理文件路径。
  2. 创建 HTTP 服务器

    • 使用 http.createServer() 方法创建一个 HTTP 服务器,并传入一个回调函数来处理每个请求。
  3. 处理请求路径

    • 获取请求的路径,并根据路径判断请求的是哪个文件。
    • 如果请求的是根路径 /,则默认返回 index.html 文件。
  4. 确定文件的内容类型

    • 通过 path.extname() 方法获取文件的扩展名。
    • 根据扩展名查找对应的内容类型,并设置响应头的 Content-Type
  5. 读取文件并返回响应

    • 使用 fs.readFile() 方法异步读取文件内容。
    • 如果文件不存在,返回 404 错误页面。
    • 如果读取文件时发生其他错误,返回 500 错误。
    • 如果文件读取成功,返回文件内容,并设置相应的 Content-Type
  6. 启动服务器

    • 使用 server.listen() 方法启动服务器,并监听指定的端口。