代码改变世界

四、Nodejs基于 Koa 框架的 WebServer 构建与原理解析 - 实践

2025-11-29 13:29  tlnshuju  阅读(0)  评论(0)    收藏  举报

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本指南系统讲解Node.js与Koa框架的核心知识,涵盖NPM安装、中间件机制、洋葱模型等关键概念,并通过实战演示模板引擎与路由配置。帮助开发者快速掌握现代化Web开发技术,构建高效可扩展的服务端应用。


提示:以下是本篇文章正文内容,下面案例可供参考

1、NPM 的安装与使用

2、框架

通过前面的基础学习,我们了解了基于 Web 的应用基本流程:
在这里插入图片描述

通过上图不难发现,无论具体应用功能如何变化, 服务端 处理任务核心三个步骤:③、④、⑤ 中,③ 和 ⑤ 的模式基本是固定的(因为HTTP协议规范了),而 ④ 是最大的变量。

如果我们每次开发一个新的应用都要把 ③ 和 ⑤ 的逻辑重新实现一遍就会特别的麻烦。所以,我们可以封装一个框架(库)把 ③ 和 ⑤ 的逻辑进行统一处理,然后通过某种方式,把 ④ 的处理暴露给框架使用者。

3、KOA的设计思想

  • 基于 NodeJS 的 web 框架,致力于 web 应用和 API 开发。
  • 由 Express 原班人马打造。
  • 支持 async。
  • 更小、更灵活、更优雅。

官网:https://koajs.com/

中文:https://koa.bootcss.com/

4、KOA的安装

# 安装 koa
npm i koa
# 或者
yarn add koa

当前最新 Koa 依赖 node v7.6.0+、ES2015+ 以及 async 的支持。

具体请关注官网说明(依赖会随着版本的变化而变化)。

参考:https://koajs.com/#introduction

  • nodemon 辅助开发工具
    npm install -g nodemon
    // package.json
    "script": {
    "start": "nodemon app.js" // app.js 为入口文件
    }
    进入 app.js 所在目录
    nodemon app.js

5、WebServer的创建

// #C5-1-1
// require koa
const Koa = require('koa');
// 初始化一个 koa 对象(Application)
const app = new Koa();
// 使用 app 对象来创建一个 webserver
/**
* http.createServer((req, res) => {})
*
* (new Http.Server().on('request', (req, res) => {}))
*/
app.listen(8888);

在这里插入图片描述

6、中间件

在这里插入图片描述

中间件

Koa 中,中间件 本质上就是用来完成各种具体业务逻辑的函数。KoaApplication 对象使用一个 middleware 来存储各种中间件函数。

6-1、注册中间件

中间件函数我们需要通过 Applicationuse 方法来进行注册:

中间件本质是函数,但不能通过 MiddleWare 去 push,因为不能保证 push 进去的是函数,而为了容错去处理,它封装了一个 use() 方法。(use 判断传入的是不是函数)
和事件注册概念一样

// #C6-1-1
const Koa = require('koa');
const app = new Koa();
app.use(() => {
console.log('中间件 - 1');
});
app.listen(8888);

7、上下文对象

KoaRequestResponse 进行了二次封装,提供了更多的特性和方法来完成工作。同时又封装了一个 Context 对象,KoaApplicationRequestResponse 对象后续其它一些特性方法挂载到该对象上,并在 中间件 执行过程中通过第一个参数进行传入:

// #C7-1-1
const Koa = require('koa');
const app = new Koa();
app.use((ctx) => {
console.log('中间件 - 1');
ctx.body = ' 这是返回的内容';
});
app.listen(8888);

我们只需要把要返回的内容赋值给 ctx.response.body 属性(简写:ctx.body)即可,Koa 会在最后进行一些处理并 write

8、洋葱模型

在这里插入图片描述

最外层中间件做通用任务处理。(涉及 promise,因为可能会有异步任务)

9、Compose

通常,我们的业务是由一系列的完成不同任务的中间件函数组合而成,那么 Koa 如何去执行这些中间件就是我们需要了解的。

// #C9-0-1
const Koa = require('koa');
const app = new Koa();
app.use((ctx) => {
console.log('中间件 - 1');
ctx.body = ' 11111';
});
app.use((ctx) => {
console.log('中间件 - 2');
ctx.body = ' 2222';
});
app.use((ctx) => {
console.log('中间件 - 3');
ctx.body = ' 3333';
});
app.listen(8888);

上述代码执行的结果是:只有第一个中间件被执行了 。原因在于 Koa 并非直接通过循环调用的方式去执行每一个中间件,而是在执行当前中间件的时候会把下一个中间件函数作为第二个参数传入。

// #C9-0-2
const Koa = require('koa');
const app = new Koa();
app.use((ctx, next) => {
console.log('中间件 - 1');
ctx.body = ' 11111';
});
app.use((ctx, next) => {
console.log('中间件 - 2');
ctx.body = ' 2222';
});
app.use((ctx, next) => {
console.log('中间件 - 3');
ctx.body = ' 3333';
});
app.listen(8888);

这样,我们就可以在中间件中控制下一个中间件逻辑是否可以继续进行,例如:

// #C9-0-3
const Koa = require('koa');
const app = new Koa();
// 模拟用户登录数据
let user = null;
// let user = {id: 1, username: 'zMouse'};
app.use((ctx, next) => {
if (!user) {
ctx.body = '没有权限';
} else {
next();
ctx.body = `<h1>${ctx.body}</h1>`;
}
});
app.use((ctx, next) => {
ctx.body = '大海和小蕊的照片';
});
app.listen(8888);

9-1、异步中间件

有的时候,我们的中间件逻辑中会包含一些异步任务:

// #C9-1-3
const Koa = require('koa');
const app = new Koa();
// 模拟用户登录数据
let user = null;
// let user = {id: 1, username: 'zMouse'};
app.use(async (ctx, next) => {
if (!user) {
ctx.body = '没有权限';
} else {
await next();
ctx.body = `<h1>${ctx.body}</h1>`;
}
});
app.use((ctx, next) => {
// 比如读取数据库(异步)
return new Promise((resolve, reject) => {
setTimeout(() => {
ctx.body = '大海和小蕊的照片';
resolve(); // or reject()
}, 1000)
});
});
app.listen(8888);

10、实用中间件

  • 静态文件代理:koa-static-cache
  • 路由:koa-router / @koa/router // npm i @koa/router 或 yarn add ……
  • 内容解析:koa-body

在这里插入图片描述

11、静态文件代理

通过学习学会怎么找中间件(npmjs.com)、怎么看中间件、怎么去用中间件

// #C11-0-1
const Koa = require('koa');
const koaStaticCache = require('koa-static-cache');	// npm i koa-static-cache
const app = new Koa();
app.use( koaStaticCache({
// url 中的前缀
prefix: '/public',
// url 对应的静态资源存放目录
dir: './public',
// 启用 gzip 传输压缩
gzip: true,
// 监听静态资源文件变化
dynamic: true
}) );
app.listen(8888);

参考:https://www.npmjs.com/package/koa-static-cache

12、路由

// #C12-0-1
const Koa = require('koa');
const koaStaticCache = require('koa-static-cache');	// npm i koa-static-cache
const KoaRouter = require('koa-router'); // npm i koa-router
const app = new Koa();
app.use( koaStaticCache({
// url 中的前缀
prefix: '/public',
// url 对应的静态资源存放目录
dir: './public',
// 启用 gzip 传输压缩
gzip: true,
// 监听静态资源文件变化
dynamic: true
}) );
const router = new KoaRouter();
// 路由函数注册
router.get('/', async (ctx, next) => {
ctx.body = '首页';
});
router.get('/users', async (ctx, next) => {
ctx.body = '用户列表';
});
// 注册中间件
app.use( router.routes() );
app.listen(8888);

12-1、GET 请求

参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET

12-2、POST请求

参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST

13、请求正文内容解析

// #C13-0-1
const Koa = require('koa');
const koaStaticCache = require('koa-static-cache');	// npm i koa-static-cache
const KoaRouter = require('koa-router'); // npm i koa-router
const koaBody = require('koa-body'); // npm i koa-body
const nunjucks = require('nunjucks'); // npm i nunjucks
const fs = require('fs');
let maxUserId = 2;
let users = [
{id: 1, username: 'haizi'},
{id: 2, username: 'zMouse'}
]
const app = new Koa();
app.use( koaStaticCache({
// url 中的前缀
prefix: '/public',
// url 对应的静态资源存放目录
dir: './public',
// 启用 gzip 传输压缩
gzip: true,
// 监听静态资源文件变化
dynamic: true
}) );
const router = new KoaRouter();
// 路由函数注册
router.get('/', async (ctx, next) => {
ctx.body = '首页';
});
// 有 Ajax 后悔将数据和模板引擎的渲染分开,即后端只提供数据,不关注页面
router.get('/users', async (ctx, next) => {
let templateFile = fs.readFileSync('./template/users.html').toString();
ctx.body = nunjucks.renderString( templateFile, {users} );
});
// 添加用户的页面
router.get('/add-user', async (ctx, next) => {
let templateFile = fs.readFileSync('./template/add-user.html').toString();
ctx.body = nunjucks.renderString( templateFile );
} );
// 处理 post 提交的数据
router.post('/add-user', koaBody(), async (ctx, next) => {
let {username} = ctx.request.body;
users.push({
id: ++maxUserId,
username
});
ctx.body = '添加成功';
});
// 注册中间件
app.use( router.routes() );
app.listen(8888);
  • public 目录,作为静态资源访问,不做二次操作的
  • template 目录,要通过模板解析

参考:https://www.npmjs.com/package/koa-body


总结

本文从Node.js环境配置切入,详细解析了Koa框架的设计思想与核心特性。通过剖析中间件注册、上下文对象和洋葱模型,揭示了请求处理流程的本质。实战部分结合静态资源代理、路由系统(koa-router)和请求体解析(koa-body),演示了如何集成Nunjucks模板引擎构建动态页面。全篇强调异步中间件协作与模块化开发,为掌握企业级Web应用开发奠定坚实基础。