前言
Node.js 作为 JavaScript 的运行时环境,在前端开发中已经占据了重要地位。但它的能力远不止于此,在后端开发领域同样表现出色。本文将介绍 Node.js 的模块系统,探讨 HTTP 协议基础,并通过一个完整的后端 Demo 展示如何用 Node.js 快速搭建 Web 服务。
与传统的后端语言的区别
Node.js 作为后端和传统 Java/PHP 这类后端相比,就像快餐车和大酒楼的区别。Node.js 轻便灵活,用前端熟悉的 JavaScript 就能快速搭起服务,特别适合中小型项目和实时应用;传统后端则像正规军,需要更多配置(比如 Java 的 Tomcat),但更适合处理复杂业务。简单说:想快速上线选 Node.js,要稳健可靠选传统后端,现在很多项目干脆让它们搭档干活——Node.js 处理接口和实时通信,传统后端负责核心业务!
node.js
接下来通过展现制作一个node.js的后端js代码(名为server.js)的过程,来给大家讲讲node.js是如何创建服务器工作!
Node.js 的 HTTP 服务
在 Node.js 中,我们可以使用内置的 http
模块轻松创建一个 Web 服务器。
那么如何引入这个内置的http呢,需要使用模块化引入。
Node.js 提供了两种模块化方案:
- CommonJS (require) :Node.js 早期采用的模块化方案
- ES Modules (import) :ES6 引入的更先进的模块化方案
1.使用了 CommonJS 的 require
语法来引入http
模块:
const http = require('http');
这是一种更古老的、原始的引用方式,如上所示,直接使用require关键字引入即可,可以在require()里面输入需要引入的模块,从而得到该模块对象。
2.使用ES6 Modules的import
语法来引入http
模块:
import http from 'http';
使用ES6新推出的import引入模块同样可以得到一个对象,注意,使用这种语法引入模块的方式需要在.mjs的文件中才能成功运行,.mjs 是 Node.js 中用于标识 ECMAScript 模块(ES Modules)的文件扩展名,当文件使用 import/export 语法时必须使用.mjs扩展名。
(.mjs才能运行import是长久以来的规定,有些node高版本可能会支持在.js的文件也能成功运行)
在本文的案例中,我们使用CommonJS 的 require
语法的这种语法来引入吧,大家可以随意选择!因为后面都是对这个对象进行操作,跟引入的方式关系不大
文件和路径模块
该案例除了需要引入http这个核心模块,我们还需要fs
(fileSystem文件模块)和path
(path路径模块)的帮助,才能帮我们完成这个服务器的创建,同样咱们统一用require方式引入。
const fs = require('fs'); // file system
const path = require('path') // 路径模块
HTTP 协议基础
引入了http这个核心模块之后,我们就要开始操作这个http对象了,不过在此之前请允许我向你介绍一下http的一些协议基础。 (了解过http的大佬可以跳过这一部分)
IP 地址与端口
- IP 地址:标识网络中的某台设备(如
127.0.0.1
表示本地) - 端口:标识设备上的特定服务(如
3306
通常用于 MySQL 服务)
在计算机网络中,IP地址和端口号共同构成了网络通信的基础定位系统。IP地址就像一栋大楼的门牌号,它唯一标识了网络中的某台设备(如服务器、个人电脑等)。而端口号则像是大楼里的房间号,它指定了设备上运行的特定服务。以MySQL数据库为例,它默认使用3306端口,这意味着当客户端要连接MySQL服务时,不仅要指定服务器的IP地址,还要指明3306这个"房间号"。
每个端口号背后都对应着一个正在运行的服务进程。当请求到达服务器时,操作系统会根据端口号将请求分发给对应的服务进程处理。比如Web服务通常使用80(HTTP)或443(HTTPS)端口,邮件服务可能使用25(SMTP)或110(POP3)端口。这种设计使得一台设备可以同时运行多个网络服务而互不干扰,就像一栋大楼里可以同时有多个办公室各自运作一样。
端口号的范围是0-65535,其中0-1023是公认端口(Well-Known Ports),通常分配给系统级服务使用;1024-49151是注册端口,用于用户级服务;49152-65535是动态/私有端口,供临时使用。理解IP地址和端口的关系,是掌握网络编程和服务器配置的重要基础。
请求与响应
请求组成
-
请求行:
- 方法:
GET
(获取)、POST
(提交)、PUT
(更新)、DELETE
(删除)等 - URL:统一资源定位符(协议://域名:端口/路径?查询参数#片段)
- HTTP版本:
HTTP/1.1
或HTTP/2
- 方法:
-
请求头:
Host
:目标主机User-Agent
:客户端信息Accept
:可接受的响应类型Content-Type
:请求体的媒体类型(如application/json
)
-
请求体(可选):
- POST/PUT 请求通常包含
- 可以是表单数据、JSON、XML等格式
响应组成
-
状态行:
- HTTP版本
- 状态码:
200
(成功)、404
(未找到)、500
(服务器错误)等 - 状态文本
-
响应头:
Content-Type
:响应体的媒体类型Content-Length
:响应体大小Set-Cookie
:设置客户端cookie
-
响应体:
- 请求的资源或处理结果
- 可能是HTML、JSON、二进制数据等
状态码分类
-
1xx(信息性):请求已被接收,继续处理
-
2xx(成功):
200 OK
:标准成功响应201 Created
:资源创建成功204 No Content
:成功但无返回内容
-
3xx(重定向):
301 Moved Permanently
:永久重定向302 Found
:临时重定向304 Not Modified
:资源未修改(缓存相关)
-
4xx(客户端错误):
400 Bad Request
:请求语法错误401 Unauthorized
:需要认证403 Forbidden
:服务器拒绝请求404 Not Found
:资源不存在
-
5xx(服务器错误):
500 Internal Server Error
:通用服务器错误502 Bad Gateway
:网关/代理错误503 Service Unavailable
:服务不可用
创建服务
接下来就是正式利用http对象创建一个web服务器了: 让我们补充server.js和index.html
//server.js
const server = http.createServer((req, res) => {
if (req.method == 'GET' &&
(req.url == '/' || req.url == '/index.html')) {
fs.readFile(
path.join(__dirname,'public', 'index.html'),
(err, content) => {
if (err) {
res.writeHead(500);
res.end('Server error');
return;
}
res.writeHead(200, { 'Content-Type': 'text/html' })
res.end(content);
})
}
})
server.listen(8080);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>哈哈</h1>
</body>
</html>
此刻我们用node启动刚刚书写的server.js,然后浏览器访问localhost:8080即可看到我们所书写的html代码了,意味着我们已经用node创建了一个本地的服务器了
可以看到在终端用node启动时,不会输出任何内容,则是启动成功了
访问localhost:8080则可以成功看到我们服务器返回的内容(index.html)
让我们来解读上面的server.js代码:
// 引入必要的Node.js核心模块
const http = require('http'); // HTTP模块,用于创建web服务器
const fs = require('fs'); // 文件系统模块,用于文件读写操作
const path = require('path'); // 路径处理模块,用于安全地处理文件路径
// 创建HTTP服务器实例
const server = http.createServer((req, res) => {
/*
* 请求处理逻辑
* req: 请求对象,包含客户端请求的信息
* res: 响应对象,用于向客户端返回数据
*/
// 检查请求方法和URL路径
// 只处理GET请求,并且URL是根路径/或/index.html
if (req.method == 'GET' && (req.url == '/' || req.url == '/index.html')) {
// 使用path.join安全地拼接文件路径
// __dirname表示当前文件所在目录
// 'public'是子目录,'index.html'是目标文件
const filePath = path.join(__dirname, 'public', 'index.html');
// 异步读取文件内容
fs.readFile(filePath, (err, content) => {
// 错误处理回调
if (err) {
// 如果读取文件出错,返回500服务器错误状态码
res.writeHead(500);
// 结束响应并返回错误信息
res.end('Server error');
return; // 提前返回,不再执行后续代码
}
// 成功读取文件后:
// 1. 设置响应状态码200(成功)和Content-Type头
res.writeHead(200, { 'Content-Type': 'text/html' });
// 2. 将文件内容作为响应体发送
res.end(content);
});
}
});
server.listen(8080); //监听8080端口
/*
* 代码解读:
* 1. 模块引入部分:
* - http: 创建web服务器的核心模块
* - fs: 用于读取本地文件
* - path: 安全地处理文件路径,避免不同操作系统的路径差异问题
*
* 2. 服务器创建部分:
* - 使用http.createServer()方法创建服务器实例
* - 传入的回调函数会在每次收到请求时执行
*
* 3. 请求处理逻辑:
* - 首先检查请求方法和路径,只处理特定的GET请求
* - 使用path.join()安全拼接路径,比字符串拼接更可靠
* - fs.readFile()异步读取文件内容
* - 完善的错误处理:文件读取失败返回500错误
* - 成功读取后设置正确的Content-Type并返回文件内容
*
* 4. 注意事项:
* - 这是一个基础实现,实际应用中需要考虑更多情况:
* * 其他HTTP方法的处理
* * 其他URL路径的处理
* * 更完善的错误处理
* * 静态资源缓存等性能优化
* - 目前代码只能处理index.html,实际需要支持多种静态文件
*/
它的特点如下:
- 模块化设计:合理使用Node.js核心模块(http, fs, path)各司其职
- 安全路径处理:使用path.join()代替字符串拼接,避免跨平台问题
- 基本路由功能:通过判断req.url实现简单路由
- 错误处理机制:对文件读取操作进行了错误处理
- 正确的Content-Type设置:确保浏览器能正确解析HTML内容
总结
本文详细介绍了如何使用 Node.js 的核心模块(http
、fs
、path
)构建一个简单的静态文件服务器,并深入解析了代码的关键部分。以下是核心要点总结:
-
核心模块的作用
http
:用于创建 Web 服务器,处理 HTTP 请求和响应。fs
:提供文件系统操作,如读取 HTML 文件。path
:安全地拼接路径,避免跨平台兼容性问题(如 Windows 和 Linux 的路径分隔符差异)。
-
服务器基本逻辑
- 使用
http.createServer()
创建服务器,监听请求。 - 检查请求方法(
GET
)和路径(/
或/index.html
)。 - 使用
fs.readFile()
异步读取文件,并返回给客户端。
- 使用
-
关键细节
- 路径处理:使用
path.join(__dirname, 'public', 'index.html')
确保路径正确。 - 错误处理:文件读取失败时返回
500
状态码,防止服务器崩溃。 - 响应头设置:正确设置
Content-Type: text/html
,让浏览器正确解析 HTML。
- 路径处理:使用
-
扩展方向
- 支持更多文件类型(如 CSS、JS、图片)。
- 添加 404 处理,优化错误提示。
- 引入流(
fs.createReadStream
)提升大文件传输性能。
本文的代码虽然简单,但涵盖了 Node.js Web 开发的核心概念,是学习后端开发的重要基础。理解这些内容后,可以进一步学习 Express、Koa 等框架,构建更强大的 Web 应用。