前端存储与后端服务的奇妙冒险:一个Node.js服务器的诞生记(cookie实现用户登入)

🌈 前端存储与后端服务的奇妙冒险:一个Node.js服务器的诞生记

从前端存储到后端服务,一次完整的Web请求是如何在底层翩翩起舞的?

存储世界的奇妙分层

在Web开发的奇幻世界里,存储分为不同的王国:

🍪 前端存储王国

  • Cookie:小小的饼干,记录着用户的登录状态和购物车信息
  • localStorage:可靠的本地仓库,能长期保存数据
  • sessionStorage:会话专属的临时储物柜
  • IndexedDB:强大的前端数据库,能存储结构化数据

🏰 后端存储王国

  • MySQL:经典的关系型数据库城堡
  • NoSQL:灵活的非关系型数据库王国
  • MongoDB:文档型数据库的魔法世界

⚡ 缓存精灵

  • 在前后端之间快速穿梭,加速数据传递的使者

Node.js:前后端之间的桥梁

node.js是什么

Node.js 就像一个能让 JavaScript 语言在电脑后台「搞事情」的超级引擎,原本 JavaScript 只能在浏览器里「画画网页、动效」,现在有了它,JavaScript 能直接在电脑里「管文件、开服务器、连数据库」,彻底跳出浏览器限制!

用生活场景秒懂 Node.js
1. 它是「快递中转站」:处理大量请求不卡顿
  • 传统服务器像「人工分拣快递」:来一个请求就派一个人专门处理(多线程),人多了会挤爆(内存占用高)。
  • Node.js 像「智能传送带」:用「事件循环」单线程处理所有请求,来快递先登记(排队),传送带逐个处理(非阻塞 I/O),哪怕同时来 1000 个快递也不慌,适合处理大量「查快递进度」这种轻任务(I/O 密集型场景)。
2. 它是「前后端的翻译官」
  • 前端工程师用 JavaScript 写网页,后端工程师以前得学 Java/Python 写服务器,现在用 Node.js 后,前后端都能用 JavaScript,就像两个人终于能说同一种语言,开发效率翻倍!
核心能力拆解
1. 能在电脑里「开商店」(搭服务器)
  • 以前开商店(建网站)需要租门面(买服务器)、雇店员(写后端代码),现在用 Node.js,只要在自己电脑输入几行代码,就能瞬间开个「线上商店」:

    javascript 复制代码
    // 开店代码(server.js)
    const http = require('http'); // 相当于「开店工具包」
    http.createServer((顾客req, 店员res) => {
      res.end('欢迎光临,这是你的商品!'); // 给顾客递东西(返回网页内容)
    }).listen(8080); // 商店开在 8080 号门(端口)

    运行后访问 http://localhost:8080,就能看到商店(服务器)的回应。

2. 能「翻箱倒柜」(操作文件)
  • 比如把电脑里的照片批量重命名:

    javascript 复制代码
    const fs = require('fs'); // 文件操作工具
    fs.readdir('./照片', (err, files) => { // 读「照片」文件夹
      files.forEach(file => {
        const newName = '美景_' + file;
        fs.rename(`./照片/${file}`, `./照片/${newName}`, err => {
          if (!err) console.log(`${file} 已重命名为 ${newName}`);
        });
      });
    });
3. 能「打电话」(连数据库、调接口)
  • 比如从「用户数据库」里查张三的信息:

    javascript 复制代码
    const mysql = require('mysql'); // 数据库电话本
    const connection = mysql.createConnection({
      host: '数据库地址',
      user: '用户名',
      password: '密码',
      database: '用户库'
    });
    connection.query('SELECT * FROM 用户 WHERE 姓名 = "张三"', (err, results) => {
      console.log(results[0].年龄); // 输出张三的年龄
    });
为什么它对普通人很重要?
  • 你刷的抖音、逛的淘宝,背后可能有 Node.js:比如抖音的直播弹幕系统,需要同时处理百万用户发弹幕,Node.js 的「非阻塞」特性让弹幕不卡顿。
  • 你用的手机 APP 背后也可能有它:比如某外卖 APP 的「附近商家」接口,用 Node.js 能快速返回商家列表,不用等很久。
  • 你电脑里的「神器工具」很多是它做的:比如「一键压缩图片」的桌面软件、「自动生成 PPT」的小工具,很多都是用 Node.js 开发的。
和浏览器 JavaScript 的区别
场景 浏览器 JavaScript(网页里的 JS) Node.js JavaScript(后台的 JS)
能力范围 只能在网页里「画画」(改文字颜色、动效)、「问用户问题」(弹对话框) 能在电脑里「翻文件」「开服务器」「连数据库」,像个全能管家
运行位置 必须打开网页才运行(在浏览器里) 直接在电脑后台运行(像 QQ 一样后台运行)
经典例子 点击按钮让图片动起来 把电脑里所有图片压缩后发到邮箱
总结:Node.js 就是「JavaScript 的超能力扩展包」

它让原本只能在浏览器「玩小游戏」的 JavaScript,变成了能在电脑里「干重活」的全能选手,无论是搭网站、做工具、连数据库,都能用同一门语言搞定,大大降低了开发门槛 ------ 这也是为什么现在很多程序员都爱用它的原因!

模块化的双生子

在JavaScript的世界里,有两种模块化方式:

javascript 复制代码
// CommonJS 方式 - Node.js的传统方式
const http = require('http');

// ES6 方式 - 现代JavaScript的方式
import http from 'http';

有趣的是,Node.js最初只支持CommonJS,但随着发展,现在也拥抱了ES6模块化。文件后缀的差异:

  • .js:默认使用CommonJS
  • .mjs:明确使用ES6 module(es6模块化)
.js与.mjs区别
特性 .js (CommonJS) .mjs (ES6 Modules)
模块语法 require()module.exports importexport
路径要求 可省略扩展名(如 ./utils 必须写完整路径(如 ./utils.mjs
顶层 await ❌ 仅支持在 async 函数中 ✅ 可直接在模块顶层使用
全局变量 __dirname__filename 无,需通过 import.meta.url 计算
JSON 导入 直接 require('./data.json') import data from './data.json' assert { type: 'json' }
与其他模块互操作 可直接 require('.mjs') 需动态导入:import('./.js').then(...)

建议使用es6 Module模块化,它支持静态分析(如 Tree-shaking 减小打包体积),前后端语法统一且与现代工具链(TypeScript、Vite)无缝集成,同时提供顶层 await 和动态导入 import() 简化异步逻辑,并且符合 Node.js 向 ES6 模块全面迁移的未来趋势。

端口的秘密通道

1.端口的核心概念

端口(Port)是计算机网络中用于标识不同应用程序或服务的数字标识符,范围从 0 到 65535。它的作用类似于一栋大楼中的 "房间号",当网络请求到达设备(IP 地址)后,系统通过端口号将数据转发给对应的应用程序。

2.为什么需要端口?
  • 复用 IP 地址:同一设备(IP)可同时运行多个服务(如 Web 服务器、数据库、邮件服务),通过端口区分。

  • 唯一标识服务:每个服务绑定特定端口,确保请求被正确路由。例如:

    • 80 端口默认分配给 HTTP 服务(如 Nginx、Apache)。
    • 443 端口用于 HTTPS(加密的 Web 服务)。
    • 3306 端口对应 MySQL 数据库服务。
3.域名 → IP → 端口 的详细流程

http://localhost:8080 为例:

端口背后对应一个服务,一个服务对应一个进程(资源),进程的背后是线程(执行)

域名domain(localhost) -> ip地址(127.0.0.1) -> -> 某台设备 -> port 设备上的某个服务(进程)

一台设备上可以有很多端口使用,有多个http服务运行多个网站

  • 域名解析
    localhost 是预定义的本地域名,直接映射到 127.0.0.1(无需 DNS 查询)。

  • 网络请求

    浏览器向 127.0.0.1:8080 发送 HTTP 请求,数据包包含:

    • 源地址 :客户端 IP + 临时端口(如 192.168.1.5:56789)。
    • 目标地址127.0.0.1:8080
  • 路由与转发

    • 数据包通过网络到达本地设备(127.0.0.1)。
    • 操作系统根据目标端口 8080,将请求转发给监听该端口的进程(如 Node.js 服务器、Tomcat)。
  • 服务处理

    监听 8080 端口的应用程序接收请求,处理后返回响应,路径反之。

4.注意事项
  • 端口占用 :若多个应用尝试绑定同一端口,会导致冲突(如启动两个监听 8080 的服务)。

  • 防火墙 :需开放特定端口才能让外部访问(如 80/443 用于 Web 服务器)。

  • 安全风险:暴露敏感端口(如数据库端口)可能导致攻击,建议限制访问或使用防火墙。

🌟 Node.js服务器诞生记

让我们跟随这段代码,一起探索一个Node.js服务器(后端)的诞生过程:

javascript 复制代码
// 引入必要的魔法工具包
const http = require('http'); // 网络通信的魔法书
const fs = require('fs');    // 文件系统的魔杖
const path = require('path'); // 路径导航的罗盘

// 创建服务器:一个待命的服务精灵
const server = http.createServer((req, res) => {
    // 请求(req)和响应(res)在这里交流
    
    // 魔法路由:根据请求的路径分派任务
    if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
        // 读取首页的魔法书
        console.log('正在寻找魔法书位置:', 
            __dirname, 'public', 'index.html', 
            path.join(__dirname, 'public', '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); // 将魔法书内容送回
        });
    }
    
    // 处理CSS魔法药剂
    else if (req.method === 'GET' && req.url === '/style.css') {
        fs.readFile(path.join(__dirname, 'public', 'style.css'), (err, content) => {
            if (err) {
                res.writeHead(500);
                res.end('Server Error');
                return;
            }
            res.writeHead(200, {'Content-Type': 'text/css'});
            res.end(content);
        });
    }
    
    // 处理JavaScript魔法咒语
    else if (req.method === 'GET' && req.url === '/script.js') {
        fs.readFile(path.join(__dirname, 'public', 'script.js'), (err, content) => {
            if (err) {
                res.writeHead(500);
                res.end('Server Error');
                return;
            }
            res.writeHead(200, {'Content-Type': 'text/javascript'});
            res.end(content);
        });
    }
});

// 服务器在8080港口开始守望
server.listen(8080, () => {
    console.log('服务器已在 http://localhost:8080 上启航!🚢');
});

🧙‍♂️ 魔法解析:代码背后的秘密

1. 核心模块的召唤

这些都是node的内置核心模块,node自带,所以无需额外下载,只需要引入即可

javascript 复制代码
const http = require('http');
const fs = require('fs');
const path = require('path');
  • http:创建HTTP服务器的核心魔法
  • fs:操作文件系统的魔杖
  • path:安全处理路径的罗盘

2. 创建服务器:服务精灵的诞生

javascript 复制代码
const server = http.createServer((req, res) => { ... });

这里诞生了一个服务精灵,它时刻准备着处理请求(req)和发送响应(res)

3. 路由魔法:请求的分派中心

javascript 复制代码
if (req.method === 'GET' && req.url === '/') { ... }
  • req.method:请求的方法(GET、POST等)
  • req.url:请求的路径(如'/'、'/style.css')

4. 文件读取的魔法

javascript 复制代码
fs.readFile(path.join(__dirname, 'public', 'index.html'), (err, content) => { ... }
  • __dirname:当前魔法书所在的目录
  • path.join():安全拼接路径的魔法
  • public 通常代表项目中的静态资源目录,用于存放客户端(浏览器)可直接访问的文件,如 CSS、JavaScript、图片等。
  • 回调函数:异步魔法的核心,文件读取完成后触发

5. 响应的艺术

javascript 复制代码
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(content);
  • writeHead():设置响应头和状态码
    • 200:一切正常的魔法信号
    • Content-Type:告诉浏览器如何处理内容
  • end():结束响应并发送内容

6. 错误处理:魔法的安全网

javascript 复制代码
if (err) {
    res.writeHead(500);
    res.end('Server Error');
    return;
}

当魔法出错时(如文件不存在),发送500错误信号,保护服务器不崩溃

7. req与res解析

1. 请求对象 req

req 是客户端(如浏览器)发送给服务器的信息,包含三个核心部分:

1.1 请求行(Request Line)

请求的第一行,格式为:METHOD PATH HTTP_VERSION

  • METHOD :HTTP 方法(如 GETPOSTPUTDELETE)。
  • PATH :请求的路径(如 /users/api/products)。
  • HTTP_VERSION :HTTP 协议版本(如 HTTP/1.1)。

示例

http 复制代码
GET /home HTTP/1.1
1.2 请求头(Request Headers)

紧随请求行后的键值对,包含元数据(如浏览器类型、缓存策略、内容类型等)。

常见请求头:

  • User-Agent:客户端信息(如 Mozilla/5.0 (Windows NT 10.0))。
  • Content-Type:请求体的格式(如 application/json)。
  • Cookie:客户端存储的会话信息。
  • Authorization:认证凭证(如 Token)。

示例

http 复制代码
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
Content-Type: application/json
1.3 请求体(Request Body)

可选部分,用于携带数据(如表单提交、JSON 数据)。

  • GET 请求:通常没有请求体。

  • POST/PUT 请求 :常见格式有 application/jsonapplication/x-www-form-urlencoded

示例(JSON 请求体)

json 复制代码
{
  "username": "john",
  "password": "123456"
}
2. 响应对象 res

res 是服务器返回给客户端的信息,同样包含三个核心部分:

2.1 响应行(Status Line)

响应的第一行,格式为:HTTP_VERSION STATUS_CODE STATUS_MESSAGE

  • HTTP_VERSION :协议版本(如 HTTP/1.1)。
  • STATUS_CODE :状态码(如 200 成功、404 未找到、500 服务器错误)。
  • STATUS_MESSAGE :状态描述(如 OKNot Found)。

示例

http 复制代码
HTTP/1.1 200 OK
2.2 响应头(Response Headers)

服务器返回的元数据,控制客户端行为(如缓存、内容类型、跨域设置等)。

常见响应头:

  • Content-Type:响应体的格式(如 text/htmlapplication/json)。
  • Cache-Control:缓存策略(如 max-age=3600)。
  • Set-Cookie:在客户端设置 Cookie。
  • Access-Control-Allow-Origin:跨域资源共享(CORS)设置。

示例

http 复制代码
Content-Type: application/json
Cache-Control: no-cache
Set-Cookie: session_id=123456
2.3 响应体(Response Body)

服务器返回的实际数据(如 HTML 页面、JSON 数据、图片等)。

示例(JSON 响应体)

json 复制代码
{
  "success": true,
  "data": {
    "id": 1,
    "name": "Product A"
  }
}
Node.js 中的 reqres 对象

http.createServer 回调中:

req 对象常用属性和方法
  • req.method:HTTP 方法(如 GETPOST)。
  • req.url:请求路径(如 /home)。
  • req.headers:请求头对象(如 req.headers['content-type'])。
  • req.on('data', callback):监听请求体数据(流式处理)。
  • req.on('end', callback):请求体接收完毕的回调。
res 对象常用属性和方法
  • res.statusCode:设置状态码(如 res.statusCode = 404)。
  • res.setHeader(name, value):设置响应头(如 res.setHeader('Content-Type', 'application/json'))。
  • res.write(data):写入响应体数据。
  • res.end():结束响应并发送数据(可选参数:res.end('Hello World'))。

示例代码:处理请求和响应

javascript 复制代码
const http = require('http');

const server = http.createServer((req, res) => {
  // 1. 解析请求行和请求头
  console.log(`请求方法: ${req.method}`);
  console.log(`请求路径: ${req.url}`);
  console.log(`请求头:`, req.headers);

  // 2. 处理请求体(适用于 POST/PUT)
  let body = '';
  req.on('data', (chunk) => {
    body += chunk;
  });
  req.on('end', () => {
    // 3. 设置响应头
    res.setHeader('Content-Type', 'application/json');
    res.setHeader('Access-Control-Allow-Origin', '*');
    
    // 4. 设置状态码
    res.statusCode = 200;
    
    // 5. 构建响应体
    const responseData = {
      message: 'Hello from server!',
      method: req.method,
      path: req.url,
      body: body ? JSON.parse(body) : {}
    };
    
    // 6. 发送响应
    res.end(JSON.stringify(responseData));
  });
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});
总结
  • 请求(req :客户端 → 服务器,包含 请求行(方法 + 路径)、请求头(元数据)、请求体(数据)
  • 响应(res :服务器 → 客户端,包含 响应行(状态码)、响应头(元数据)、响应体(数据)
  • Node.js 处理流程:解析请求 → 处理业务逻辑 → 设置响应头和状态码 → 返回响应体。

🌈 前端与后端的存储探戈

当浏览器收到响应后,前端的存储魔法开始发挥作用:

graph TD A[HTML响应] --> B[构建DOM树] C[CSS响应] --> D[构建CSSOM树] B --> E[渲染树] D --> E E --> F[布局] F --> G[绘制] J[JavaScript] --> H[执行JavaScript] H --> I[操作DOM/CSS]

在这个过程中,前端存储扮演着重要角色:

  1. Cookie:自动随请求发送,用于身份验证
  2. localStorage:持久保存用户偏好设置
  3. sessionStorage:临时保存标签页数据
  4. IndexedDB:存储大量结构化数据
js 复制代码
if(req.method == 'POST' && req.url == '/login'){//首页
       //用户名和密码的校验
       res.writeHead(200,{
        //服务器端设置的cookie
        'Set-Cookie':"username=admin;",
        'Content-Type':"application/json"
       })
        res.end(JSON.stringify({success:true,mag:"登入成功"}))
    }
    if(req.method == 'GET' && req.url == '/check-login'){
        if(req.headers.cookie){
            res.writeHead(200,{
                contentType:'application/json'
            })
            res.end(JSON.stringify({
                loggedIn:true,
                username:'admin'    
            }))
        }else{
            res.writeHead(200,{
                contentType:'application/json'
            })
            res.end(JSON.stringify({
                loggedIn:false,
                username:''    
            }))
        }
        
    }
javascript 复制代码
// 服务器端设置 Cookie
if(req.method == 'POST' && req.url == '/login'){
    res.writeHead(200,{
        'Set-Cookie': "username=admin;",  // ← 核心设置语句
        'Content-Type': "application/json"
    })
    res.end(JSON.stringify({success:true,msg:"登入成功"}))
}

底层机制

  • Set-Cookie 响应头指示浏览器存储指定的 Cookie
  • 格式为键值对:username=admin
  • 未设置过期时间,默认为会话 Cookie(关闭浏览器即失效)
  • 未指定路径,默认为当前路径(/
2. 浏览器自动存储

浏览器收到响应后:

  1. 解析 Set-Cookie 头部
  2. 将键值对存储在内存或硬盘中(按域名隔离)
  3. 存储位置:浏览器 Cookie 存储区
  4. 最大容量:约 4KB(单个域名下)
javascript 复制代码
// 浏览器发起请求时自动携带 Cookie
if(req.method == 'GET' && req.url == '/check-login'){
    if(req.headers.cookie){  // ← 浏览器自动注入
        // 处理 Cookie 逻辑
    }
}

底层机制

  1. 浏览器检查当前请求的域名和路径

  2. 匹配有效的 Cookie(未过期+域名路径匹配)

  3. 自动添加到请求头的 Cookie 字段:

    http 复制代码
    GET /check-login HTTP/1.1
    Cookie: username=admin  // 自动携带
4. 存储位置与结构

浏览器内部存储结构(伪代码表示):

javascript 复制代码
cookies = {
  "localhost:8080": {
    "/": [
      {
        name: "username",
        value: "admin",
        expires: null, // 会话 Cookie
        httpOnly: false,
        secure: false
      }
    ]
  }
}
5. 完整生命周期
sequenceDiagram participant Client as 浏览器 participant Server as 服务器 Client->>Server: POST /login (用户名密码) Server->>Client: 响应头: Set-Cookie: username=admin Note right of Client: 1. 存储Cookie到内存
2. 域名隔离存储 Client->>Server: GET /check-login Note left of Client: 自动添加请求头:
Cookie: username=admin Server->>Client: 返回登录状态 Client->>Server: 后续所有请求 Note left of Client: 持续携带Cookie
直到浏览器关闭
6. 安全特性

为避免安全隐患,建议添加:

javascript 复制代码
// 更安全的Cookie设置
res.writeHead(200,{
    'Set-Cookie': 
        `username=${encodeURIComponent(username)}; ` + 
        'HttpOnly; ' +       // 禁止JS访问
        'SameSite=Lax; ' +   // 基本跨站防护
        'Path=/; ' +         // 作用路径
        'Max-Age=3600',      // 1小时有效期
    
    'Content-Type': "application/json"
})
7. 浏览器开发者工具验证

在Chrome开发者工具中:

  1. Application > Storage > Cookies 查看存储的Cookie
  2. Network 标签查看请求/响应头中的Cookie传输

核心原理 :Cookie 通过 Set-Cookie 响应头设置,浏览器自动存储并在后续请求中通过 Cookie 请求头发送,实现了 HTTP 无状态协议的身份保持功能。

🚀 开发小贴士:魔法师的工具箱

  1. 自动重启魔法

    bash 复制代码
    npm install -g nodemon
    nodemon server.js

    让服务器在代码变化时自动重启

  2. 路径的奥秘

    javascript 复制代码
    console.log(path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'));
    // 输出: '/foo/bar/baz/asdf'

    path.join() 会正确处理路径分隔符和..父目录

  3. 内容类型的魔法语言

    文件类型 Content-Type
    HTML text/html
    CSS text/css
    JavaScript text/javascript
    JSON application/json
    图片 image/*

🌟 总结:存储与服务的和谐交响

从前端存储到后端服务,我们完成了一次完整的Web魔法之旅:

  • 前端存储:在用户浏览器中保存数据的小精灵
  • 后端服务:处理请求、读取文件、发送响应的魔法师
  • HTTP协议:连接前后端的魔法通信协议

理解这些底层原理,就如同掌握了Web开发的魔法语言。无论是存储用户数据的前端精灵,还是处理请求的后端魔法师,它们共同构建了我们每天使用的互联网世界。

下次当你打开一个网页时,不妨想象一下背后这些奇妙的小精灵们是如何协同工作的!✨

相关推荐
gnip3 分钟前
项目开发流程之技术调用流程
前端·javascript
答案—answer3 分钟前
three.js编辑器2.0版本
javascript·three.js·three.js 编辑器·three.js性能优化·three.js模型编辑·three.js 粒子特效·three.js加载模型
转转技术团队16 分钟前
多代理混战?用 PAC(Proxy Auto-Config) 优雅切换代理场景
前端·后端·面试
南囝coding18 分钟前
这几个 Vibe Coding 经验,真的建议学!
前端·后端
阿杆19 分钟前
服务一挂就手忙脚乱?教你用 Amazon Lambda 打造 0 成本服务监控!
后端·自动化运维
gnip31 分钟前
SSE技术介绍
前端·javascript
yinke小琪1 小时前
JavaScript DOM节点操作(增删改)常用方法
前端·javascript
德育处主任1 小时前
在亚马逊云上,如何基于 VPC IPAM 的 ALB 公网 IP 预测分配?
后端
枣把儿1 小时前
Vercel 收购 NuxtLabs!Nuxt UI Pro 即将免费!
前端·vue.js·nuxt.js
望获linux1 小时前
【Linux基础知识系列】第四十三篇 - 基础正则表达式与 grep/sed
linux·运维·服务器·开发语言·前端·操作系统·嵌入式软件