Nodejs Express框架开发基础
Nodejs Express框架开发基础

Nodejs Express框架开发基础

以新闻管理系统为例:

1、初始化项目

(1)初始化包管理配置文件:npm init

(2)安装express框架:npm install express

(3)创建app.js文件

// 导入express框架
const express = require('express')
// 创建express服务区实例对象
const app = express()
// 设置端口号并启动服务器
app.listen(80,()=>{
    console.log('服务器启动成功,请访问http://127.0.0.1')
})

上述代码中只创建了基本服务器,当前并未接收到任何客户端请求。当前项目架构:

2、配置常用中间件

(1)配置跨域中间件

cors中间件:解决前端向后端的跨域请求,什么是跨域呢?只要请求的 协议(https/http)、域名、端口 中有任何一个不一致,浏览器就会认为这是跨域请求。

在项目中,可以运行npm install cors来安装cors中间件。

在app.js文件中导入cors中间件,并进行配置。

// 导入中间件
const cors = require('cors')
// 使用app.use注册成为全局中间件
app.use(cors())

(2)配置解析表单数据的中间件

Express框架中内置:urlencoded。在Koa框架中,通常用第三方中间件:koa-bodyparser。

在 Express 中,app.use(express.urlencoded({ extended: false })) 这行代码的作用是解析 POST 请求体中 application/x-www-form-urlencoded 格式的数据(也就是传统的表单提交格式)。

extended 参数决定了使用哪个库来解析这些数据,它的核心区别在于数据的复杂程度

extended: false -> 使用 Node.js 内置的 querystring

extended: true -> 使用第三方的 qs

通常,根据前端传入的不同数据格式,需要使用不同的解析库,甚至是自定义中间件。

3、创建路由模块

根目录下新建router和router_handler文件夹,用来存放路由模块。其中,router文件夹用来存放客户端请求地址和事件处理函数之间的映射关系;router_handler文件夹用来存放路由事件处理函数。

(1)初始化路由模块

在 router 文件夹中创建 user.js 文件,用于保存所有和用户相关的路由对应关系,初始化代码如下:

const express = require('express')
// 创建路由对象
const router = express.Router()

// 注册 API 接口
router.post('/register', (req, res) => {
  res.send({ status: 0, message: 'register OK' })
})

// 共享路由对象
module.exports = router

路由模块定义成功之后,就可以在 app.js 入口文件中导入路由模块了。示例代码如下:

// 导入路由自定义模块,./router为当前目录
const user_router = require('./router/user') 
// 将路由模块注册成全局中间件
app.use('/api', user_router)

app.use('/api', user_router) 代码段应位于跨域中间件和解析表单数据中间件之后。

(2)抽离路由模块事件处理函数

上述写法非常不利于后期维护,有时一个事件处理函数就需要几十行代码,页面会显得很杂乱。规范的写法是将事件处理函数单独封装成一个模块,实现思路如下:

在 router_handler 目录下,新建 user.js 文件,存放和用户相关的事件处理函数。

在 user.js 文件中,使用 exports 往外共享事件处理函数,供其他模块使用。

首先将注册的事件处理函数抽离到 user.js 文件中,代码如下:

exports.register = (req, res) => {
  res.send({ status: 0, message: 'register OK' })
}

事件处理函数模块定义完成之后,在 user.js 文件中导入新模块,并调用 register 方法。最终抽离完成的代码如下:

const express = require('express')
// 导入事件处理函数模块
const userFn = require('../router_handler/user')
const router = express.Router()

// 使用 userFn 模块下的 register 方法
router.post('/register', userFn.register)

module.exports = router

4、配置数据库信息

npm install mysql:安装mysql第三方模块
新建db文件夹,在db文件夹下新建index.js,用来创建数据库连接对象,示例代码如下:

// 导入 mysql 模块
const mysql = require('mysql')
// 创建数据库连接对象
const db = mysql.createPool({
  // 数据库 IP 地址
  host: '5.252.164.181',
  // 数据库账号
  user: 'webedu',
  // 数据库密码
  password: '5b2NxdXGBmKN3H8c',
  // 数据库名称
  database: 'webedu'
})
// 共享数据库连接对象
module.exports = db

在开发初期,SQL 语句通常硬编码在 Controller(事件处理函数)中。但为了提高数据复用性并实现关注点分离,我们会将数据持久化逻辑抽离到 Model 层(或 DAO 层)。同理,为了沉淀核心业务逻辑并减少冗余,我们会将跨模块复用的复杂逻辑下沉到 Service 服务层,从而实现高内聚、低耦合的代码结构。

5、MVC架构

(1)Model(模型):负责数据和业务逻辑。它直接与数据库打交道,定义数据的增删改查。它只关心数据长什么样,不关心数据怎么显示。

(2)View:在前后端分离的架构中,View 被移到了前端(如 Vue 或 React)。后端不再负责渲染页面,而是通过 res.send() 返回 JSON 数据。前端拿到 JSON 后,再由前端框架负责“视图”的展示。

(3)Controller(控制器):负责转发请求业务处理

6、主流的用户认证方式

认证方式存储位置状态感典型场景
Session-Cookie服务端内存/Redis有状态传统的管理后台、单体架构
JWT客户端(Local/Cookie)无状态前后端分离、App、微服务
OAuth 2.0第三方服务器授权机制社交登录、开放平台
Passkeys设备硬件安全性极高银行、高安全级别账户

在JWT认证方式中,客户端手动将 Token 放在 HTTP 头部(Authorization: Bearer )传给服务器。

7、请求方法(Methods)

特性GET (获取)POST (提交)PUT (更新)DELETE (删除)
主要作用从服务器获取资源向服务器发送数据(创建资源)更新服务器上的资源删除服务器上的资源
数据位置放在 URL 中(查询字符串)放在 Request Body(请求体)中放在 Request Body 中通常不带 Body,ID 在 URL 中
数据大小限制受限于 URL 长度(通常约 2KB)无限制(适合传文件/大数据)无限制无限制
安全性较低(参数暴露在地址栏,会被缓存)较高(数据不显示在 URL 中)较高较高
幂等性*(多次请求结果相同)(多次提交可能创建多个数据)(多次覆盖结果相同)(删一次和删多次结果相同)
可缓存性可以被浏览器缓存不可被浏览器缓存不可缓存不可缓存
后退/刷新无害数据会被重新提交(浏览器会弹出警告)无害无害

1.、GET (获取数据)

GET 请求通常将参数拼接在 URL 后面。

axios.get('/api/users', { params: { id: 123 } // 最终变为 /api/users?id=123 }).then(res => console.log(res.data));

2、POST (创建数据)

POST 会将敏感或大量的数据封装在“请求体”(Body)中。

// 注册新用户
axios.post('/api/register', {
    username: 'Geographic_Explorer',
    password: 'securepassword123'
}).then(res => console.log("注册成功"));

3、PUT (更新数据)

PUT 通常用于完整替换现有的资源。

// 修改 ID 为 101 的用户资料(全量更新)
axios.put('/api/users/101', {
    nickname: 'ZhidiYun_Dev',
    bio: 'Full-stack developer'
});

4、DELETE (删除数据)

DELETE 请求通常只需要在 URL 中指定要删除的资源 ID。

// 删除 ID 为 99 的文章
axios.delete('/api/posts/99')
    .then(() => console.log("删除成功"));

8、Express和Koa框架对比

(1)表格对比

特性ExpressKoa
异步方案回调 (Callback)async/await (Promise)
中间件模型线性流,不等待异步洋葱模型,支持 await next()
内置功能丰富(路由、静态资源等)极简(几乎所有功能需安装插件)
错误处理容易漏掉异步错误统一 try-catch 非常方便
学习曲线较低,适合快速上手较高,需要理解 async/await 和模型

(表 1)

维度Express (像多米诺骨牌)Koa (像剥洋葱)
next() 的本质“去吧,后面不关我事了”“我先等后面的做完,我再收尾”
异步捕获很难在前面的中间件获取后面异步操作的结果。极其简单,直接 await next() 即可。
错误捕获如果后面异步报错,前面很难 try-catch 到。可以在最外层中间件 try { await next() } 捕获全链路错误。

(表 2)

框架捕获异步错误的能力大白话解释
Express像单向跑步,后面的人摔了,前面的人看不见。必须手动派人(next(err))去报信。
Koa极强像拉橡皮筋,不管你拉多远(异步多久),断了(报错)的时候力都会传回到手心里。

(表 3)

(2)举例对比

F-PolyImage(AI 绘图平台)作为场景。

想象一下,用户点击“生成图片”后,后台需要经历三个步骤:

  1. 身份验证:检查用户点数够不够。
  2. AI 生成:调用模型算图(这是个耗时的异步过程)。
  3. 日志记录:图片画完了,记录一下消耗的时间和结果。

A. Express:像“单向接力赛”

在 Express 里,中间件就像一排站好的运动员。

  • 流程如下:
    • 第一个运动员(验证):检查完点数,大喊一声 next(),把接力棒传给下一个人。
    • 第二个运动员(日志):他拿到棒子,看一眼表,记录“开始时间”,然后也大喊 next() 传下去。
    • 第三个运动员(AI 算图):他接到棒子开始画画。
  • 尴尬的地方: 第二个“日志运动员”喊完 next() 后,他不会等第三个人画完。他会立刻在自己的小本本上写:“计时结束,耗时 0.001 秒”。 等到 5 秒钟后 AI 图片真的画出来了,第二个运动员早就跑去喝水了,他根本没法回头去更新那个 5 秒的真实耗时。

大白话总结: Express 就像泼出去的水,中间件一旦把请求传给下一个,就很难再管回头的事了。


B. Koa:像“剥洋葱 / 闯关模式”

在 Koa 里,中间件像是一层层的洋葱皮,请求必须“钻进去”再“钻出来”。

  • 流程如下:
    • 进入外层(日志层):记录开始时间。
    • 进入内层(AI 算图层):这一层说“等一下,我要画画”,于是整个流程就在这里等(await)
    • 画完了:图片生成成功。
    • 钻回外层(回到日志层):请求原路返回,再次经过日志层。这时候日志层一看表:“嘿,画画用了 5 秒,记下来!”
  • 优雅的地方: 你只需要在最外层写一个简单的逻辑:开始 -> 等待(next) -> 结束。无论中间 AI 画图画了多久,最外层都能稳稳地等到它结束,然后准确记录时间。

大白话总结: Koa 就像回力镖,请求发出去处理完,最后一定会回到你手里,让你做最后的收尾工作。

一条评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注