Koa.js 教程 | 一份不可多得的 Node.js 的 Web 框架 Koa.js 教程
第一章 安装和配置 koa
Koa 是一个轻量级、现代化的框架, 由 Express 原班人马开发
初始化配置文件 package.json
npm init -y
配置 package.json (ESM规范)
{
"type": "module",
"name": "demo",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev":"nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
npm 官网
安装koa
npm i koa
全局安装 nodemon
.
npm i nodemon -g当 nodemon 检测到监视的文件发生更改时, 会自动重新启动应用
第二章 创建并启动 http 服务器
中间件
中间件是处理 HTTP 请求和响应的函数,它们可以做以下操作:
- 处理请求(例如解析请求体、验证用户身份等)
- 修改响应(例如设置响应头、发送响应体等)
- 执行后续中间件
中间件 - 很重要的概念 !!!!!!!
注意 : app.use() 方法用于注册 中间件
中间件 是处理 http 请求和响应的函数 , 当一个请求到达服务器时, 会从第一个中间件开始执行, 直到最后一个中间件
上下文对象 ctx
在 Koa 中,ctx(上下文)对象是每个中间件函数的核心,它包含了请求和响应的所有信息。所有的 HTTP 请求和响应都通过 ctx 进行处理。
上下文对象 ctx ( context ) 包含了与当前 http 请求相关的所有信息
如: http方法、url、请求头、请求体、查询参数等
import Koa from 'koa'
const hostname = "127.0.0.1" //服务器监听的ip地址
const port = 8008 //服务器监听的端口号
/*
实例化一个 Koa 对象
实例化是指根据一个类创建具体对象的过程
*/
const app = new Koa()
app.use(async ctx => {
ctx.body = "juejin.cn" // 使用 ctx.body 设置响应体的内容
})
//启动 http 服务器, 并在指定的ip地址(127.0.0.1)和端口(8008)上监听连接请求
app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`)
})
第三章 洋葱模型
洋葱模型
当你处理一个请求时,
可以想象成是在 "剥洋葱" ,从外向内一层一层地往里剥,直到剥到中心部分
这个过程涉及对 请求 的多个层面进行解析、验证、处理
在处理完洋葱(请求)后,
构建 响应 的过程就像是从精心准备的食材 ( 处理请求 后得到的数据) 开始,
从内向外逐层添加调料(格式化、封装等),最终形成一道色香味俱佳的菜肴(响应)
import Koa from 'koa'
const hostname = "127.0.0.1" //服务器监听的ip地址
const port = 8008 //服务器监听的端口号
/*
实例化一个 Koa 对象
实例化是指根据一个类创建具体对象的过程
*/
const app = new Koa()
/*
app.use() 方法用于注册中间件
中间件是处理 http 请求和响应的函数
当一个请求到达服务器时, 会从第一个中间件开始执行, 直到最后一个中间件
上下文对象 ctx(context) 包含了与当前 http 请求相关的所有信息
如: http方法、url、请求头、请求体、查询参数等
*/
app.use(async (ctx,next) => {
console.log(1)
await next() //若中间件调用了next(),会暂停当前中间件的执行,将控制权传递给下一个中间件
console.log(2)
})
app.use(async (ctx,next) => {
console.log(3)
await next()
console.log(4)
})
//当中间件没有再调用next(),则不需要再将控制权传递给下一个中间件,控制权会按照相反的顺序执行
app.use(async (ctx,next) => {
console.log(5)
ctx.body = "dengruicode.com" // 使用 ctx.body 设置响应体的内容
})
//启动 http 服务器, 并在指定的ip地址(127.0.0.1)和端口(8008)上监听连接请求
app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`)
})
第四章 安装和配置路由 - get请求
在 Koa 中,koa-router 是一个轻量级的路由中间件,它可以帮助你定义路由、处理 HTTP 请求并解析请求参数。通过使用 koa-router,你可以创建一个灵活的路由系统,轻松地组织和管理 Koa 应用的各个部分。
安装 koa-router
首先,你需要安装 koa-router:
npm install @koa/router # 注意:新版 koa-router 包名是 @koa/router
import Koa from 'koa'
import Router from '@koa/router'
const hostname = "127.0.0.1"
const port = 8008
const app = new Koa()
const router = new Router() //实例化一个 Router 对象
//------ get请求
//路由是根据客户端发送的请求(包括请求的路径、方法等)调用与之匹配的处理函数
//根路由 http://127.0.0.1:8008/
router.get('/', async ctx => { //get请求
ctx.body = "dengruicode.com"
})
//查询参数 http://127.0.0.1:8008/test?id=001&web=dengruicode.com
router.get('/test', async ctx => { //get请求
let id = ctx.query.id
let web = ctx.query.web
ctx.body = id + " : " + web
})
//路径参数 http://127.0.0.1:8008/test2/id/002/web/www.dengruicode.com
router.get('/test2/id/:id/web/:web', async ctx => {
let id = ctx.params.id
let web = ctx.params.web
ctx.body = id + " : " + web
})
//重定向路由 http://127.0.0.1:8008/test3
router.redirect('/test3', 'https://www.baidu.com')
app.use(router.routes()) //将定义在 router 对象中的路由规则添加到 app 实例中
//------ 路由分组
//http://127.0.0.1:8008/user/add
//http://127.0.0.1:8008/user/del
const userRouter = new Router({ prefix: '/user' })
userRouter.get('/add', async ctx => {
ctx.body = "添加用户"
})
userRouter.get('/del', async ctx => {
ctx.body = "删除用户"
})
app.use(userRouter.routes())
// 在所有路由之后添加404处理函数
app.use(async ctx => {
if (!ctx.body) { //若没有设置 ctx.body, 则说明没有到匹配任何路由
ctx.status = 404
ctx.body = '404 Not Found'
}
})
app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`)
})
第五章 post请求
安装 koa-body
Koa 原生不支持解析 POST 请求体,需安装 koa-body 中间件:
npm install koa-body
POST 请求处理示例
修改 src/index.js,新增 POST 路由:
import Koa from 'koa';
import Router from '@koa/router';
import { koaBody } from 'koa-body';
const app = new Koa();
const router = new Router();
const port = 8008;
// 注册 koa-body 中间件:解析 JSON、表单、文件类型的 POST 数据
app.use(koaBody({
multipart: true, // 支持文件上传(后续第八章用)
json: true, // 解析 JSON 格式
urlencoded: true // 解析表单格式(application/x-www-form-urlencoded)
}));
// 1. 处理 JSON 格式 POST 请求
router.post('/api/json', async (ctx) => {
const { name, age } = ctx.request.body;
ctx.body = { // ctx.request.body 是 koa-body 解析后的 POST 数据
code: 200,
msg: "JSON 数据接收成功",
data: { name, age }
};
});
// 2. 处理表单格式 POST 请求
router.post('/api/form', async (ctx) => {
const { username, password } = ctx.request.body;
ctx.body = {
code: 200,
msg: "表单数据接收成功",
data: { username, password }
};
});
app.use(router.routes());
// 404 处理
app.use(async (ctx) => {
ctx.status = 404;
ctx.body = '404 Not Found';
});
app.listen(port, () => {
console.log(`POST 服务器启动:http://localhost:${port}`);
});
测试 POST 请求(两种方式)
方式 1:Postman 测试
-
请求地址:
http://localhost:8008/api/json -
请求方法:POST
-
请求体:选择
raw > JSON,输入:{ "name": "张三", "age": 20 } -
响应:
{"code":200,"msg":"JSON 数据接收成功","data":{"name":"张三","age":20}}
方式 2:curl 命令测试
# 测试 JSON 格式
curl -X POST -H "Content-Type: application/json" -d '{"name":"张三","age":20}' http://localhost:8008/api/json
# 测试表单格式
curl -X POST -d "username=admin&password=123456" http://localhost:8008/api/form
第六章 错误处理
import Koa from 'koa'
import Router from '@koa/router'
const hostname = "127.0.0.1"
const port = 8008
const app = new Koa()
const router = new Router()
//http://127.0.0.1:8008/
router.get('/', async ctx => {
throw new Error("测试")
})
/*
将 '错误处理中间件' 放在 '路由处理中间件' 之前, 当一个请求到达时,
会先经过 '错误处理中间件', 然后才会进入 '路由处理中间件',
是为了确保可以捕获错误
*/
app.use(async (ctx, next) => { // 错误处理中间件
try {
await next()
} catch (err) {
//console.log('err:', err)
ctx.status = 500
ctx.body = 'err: ' + err.message
}
})
app.use(router.routes()) // 路由处理中间件
app.listen(port, hostname, () => {
console.log(`服务器已启动: http://${hostname}:${port}`)
})
第七章 允许跨域请求
安装跨域中间件
npm install @koa/cors
跨域配置示例
import Koa from 'koa';
import Router from '@koa/router';
import Cors from '@koa/cors';
const app = new Koa();
const router = new Router();
const port = 8008;
app.use(Cors()) //允许跨域请求
// 测试跨域路由
router.get('/api/cors', async (ctx) => {
ctx.body = {
code: 200,
msg: "跨域请求成功"
};
});
app.use(router.routes());
app.listen(port, () => {
console.log(`跨域服务器启动:http://localhost:${port}`);
});
测试跨域
在任意前端项目(如 Vue / React / HTML 文件)中发送请求:
// 前端代码示例
fetch('http://localhost:8008/api/cors')
.then(res => res.json())
.then(data => console.log(data)) // 输出 {code:200, msg:"跨域请求成功"}
.catch(err => console.error(err));
无跨域报错即配置成功。
第八章 上传图片
依赖准备(复用 koa-body)
koa-body 已支持文件上传,无需额外安装依赖,只需确保配置 multipart: true。
图片上传示例
import Koa from 'koa';
import Router from '@koa/router';
import { koaBody } from 'koa-body';
import fs from 'fs';
import path from 'path';
const app = new Koa();
const router = new Router();
const port = 8008;
// 1. 创建上传目录(不存在则创建)
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir, { recursive: true });
}
// 2. 配置 koa-body 支持文件上传
app.use(koaBody({
multipart: true, // 开启文件上传
formidable: {
uploadDir: uploadDir, // 临时存储目录
keepExtensions: true, // 保留文件扩展名(如 .png/.jpg)
maxFieldsSize: 2 * 1024 * 1024, // 限制文件大小 2MB
filename: (name, ext, part, form) => {
// 自定义文件名:时间戳 + 原扩展名,避免重复
return Date.now() + ext;
}
}
}));
// 3. 图片上传接口
router.post('/api/upload', async (ctx) => {
// ctx.request.files 是上传的文件对象
const file = ctx.request.files.file; // 前端上传的文件字段名需为 file
if (!file) {
ctx.status = 400;
ctx.body = { code: 400, msg: "请选择上传的图片" };
return;
}
// 返回文件信息
ctx.body = {
code: 200,
msg: "图片上传成功",
data: {
filename: file.newFilename, // 自定义后的文件名
path: `/uploads/${file.newFilename}`, // 访问路径
size: file.size // 文件大小(字节)
}
};
});
// 4. 静态文件访问:让上传的图片可通过 URL 访问
app.use(async (ctx, next) => {
if (ctx.path.startsWith('/uploads/')) {
const filePath = path.join(uploadDir, ctx.path.replace('/uploads/', ''));
if (fs.existsSync(filePath)) {
ctx.type = path.extname(filePath).slice(1); // 设置响应类型(如 png/jpg)
ctx.body = fs.createReadStream(filePath); // 读取文件并返回
return;
}
ctx.status = 404;
ctx.body = "文件不存在";
return;
}
await next();
});
app.use(router.routes());
app.listen(port, () => {
console.log(`图片上传服务器启动:http://localhost:${port}`);
});
测试图片上传
方式 1:Postman 测试
- 请求地址:
http://localhost:8008/api/upload - 请求方法:POST
- 请求体:选择
form-data,Key 为file,Type 选File,上传一张图片。 - 响应:返回文件路径,如
http://localhost:8008/uploads/1738987654321.png,访问该 URL 可查看图片。
方式 2:curl 命令测试
终端输入 bash 命令
curl -X POST -F "file=@/你的图片路径/xxx.png" http://localhost:8008/api/upload
第九章 cookie
Cookie 是存储在客户端浏览器的小型文本数据,Koa 内置 ctx.cookies API 可以操作 Cookie。
Cookie 操作示例
import Koa from 'koa'
import Router from '@koa/router'
const app = new Koa();
const router = new Router();
const port = 8008;
// 1. 设置 Cookie
router.get('/cookie/set', async (ctx) => {
// ctx.cookies.set(名称, 值, 配置)
ctx.cookies.set(
'username',
encodeURIComponent('张三'),
{
maxAge: 24 * 60 * 60 * 1000, // 过期时间 1 天(毫秒)
httpOnly: true, // 仅允许服务端访问,防止 XSS 攻击
secure: false, // 开发环境设为 false(HTTPS 环境设为 true)
path: '/', // 生效路径(/ 表示全站)
sameSite: 'lax' // 防止 CSRF 攻击
}
);
ctx.body = { code: 200, msg: "Cookie 设置成功" };
});
// 2. 获取 Cookie
router.get('/cookie/get', async (ctx) => {
const username = ctx.cookies.get('username');
ctx.body = {
code: 200,
msg: "Cookie 获取成功",
data: { username }
};
});
// 3. 删除 Cookie
router.get('/cookie/delete', async (ctx) => {
ctx.cookies.set('username', '', { maxAge: 0 }); // 设置 maxAge 为 0 即删除
ctx.body = { code: 200, msg: "Cookie 删除成功" };
});
app.use(router.routes());
app.listen(port, () => {
console.log(`Cookie 服务器启动:http://localhost:${port}`);
});
测试 Cookie
- 访问
http://localhost:8008/cookie/set→ 设置 Cookie; - 访问
http://localhost:8008/cookie/get→ 获取 Cookie,输出{username: "张三"}; - 访问
http://localhost:8008/cookie/delete→ 删除 Cookie,再次获取则为undefined。
第十章 session
安装 Session 中间件
Koa 原生不支持 Session,需安装 koa-session:
npm install koa-session
Session 配置示例
import Koa from 'koa'
import Router from '@koa/router'
import session from 'koa-session'
const app = new Koa();
const router = new Router();
const port = 8008;
// 1. 配置 Session 密钥(生产环境需改为随机字符串)
app.keys = ['dengruicode_secret_key'];
// 2. Session 配置
const CONFIG = {
key: 'koa:sess', // Session Cookie 名称
maxAge: 24 * 60 * 60 * 1000, // 过期时间 1 天
autoCommit: true,
overwrite: true,
httpOnly: true, // 仅服务端访问
signed: true, // 签名 Cookie,防止篡改
rolling: false, // 不刷新过期时间
renew: false, // 快过期时自动续期
secure: false, // 开发环境 false
sameSite: 'lax'
};
// 3. 注册 Session 中间件
app.use(session(CONFIG, app));
// 4. Session 操作
// 设置 Session
router.get('/session/set', async (ctx) => {
ctx.session.user = {
id: 1,
name: "张三",
age: 20
};
ctx.body = { code: 200, msg: "Session 设置成功" };
});
// 获取 Session
router.get('/session/get', async (ctx) => {
const user = ctx.session.user;
ctx.body = {
code: 200,
msg: "Session 获取成功",
data: { user }
};
});
// 删除 Session
router.get('/session/delete', async (ctx) => {
ctx.session = null; // 清空 Session
ctx.body = { code: 200, msg: "Session 删除成功" };
});
app.use(router.routes());
app.listen(port, () => {
console.log(`Session 服务器启动:http://localhost:${port}`);
});
测试 Session
- 访问
http://localhost:8008/session/set→ 设置 Session; - 访问
http://localhost:8008/session/get→ 获取 Session,输出用户信息; - 访问
http://localhost:8008/session/delete→ 清空 Session,再次获取则为undefined。
注意:
koa-session是基于 Cookie 的内存 Session,生产环境建议使用koa-redis将 Session 存储到 Redis,避免服务重启丢失数据。
第十一章 jwt
安装 JWT 依赖
npm install jsonwebtoken koa-jwt
-
jsonwebtoken:生成 / 解析 JWT 令牌; -
koa-jwt:验证 JWT 令牌的中间件。
JWT 完整示例
import Koa from 'koa'
import Router from '@koa/router'
import jwt from 'jsonwebtoken'
import koaJwt from 'koa-jwt'
const app = new Koa();
const router = new Router();
const port = 8008;
// 1. JWT 密钥(生产环境需加密存储)
const JWT_SECRET = 'dengruicode_jwt_secret';
// JWT 过期时间:1 小时(秒)
const JWT_EXPIRES_IN = 3600;
// 2. 登录接口:生成 JWT 令牌
router.post('/api/login', async (ctx) => {
// 模拟验证用户名密码(生产环境需查数据库)
const { username, password } = ctx.request.body;
if (username === 'admin' && password === '123456') {
// 生成 JWT 令牌
const token = jwt.sign(
{ id: 1, username }, // 载荷:存储用户信息(不要存敏感数据)
JWT_SECRET,
{ expiresIn: JWT_EXPIRES_IN }
);
ctx.body = {
code: 200,
msg: "登录成功",
data: { token }
};
} else {
ctx.status = 401;
ctx.body = { code: 401, msg: "用户名或密码错误" };
}
});
// 3. 受保护的接口:需要 JWT 验证
// koa-jwt 中间件会自动解析 Authorization 头中的 token
app.use(koaJwt({ secret: JWT_SECRET }).unless({
path: [/^/api/login/] // 排除登录接口,无需验证
}));
// 4. 获取用户信息接口(需验证 JWT)
router.get('/api/user/info', async (ctx) => {
// ctx.state.user 是 koa-jwt 解析后的 JWT 载荷
const { id, username } = ctx.state.user;
ctx.body = {
code: 200,
msg: "获取用户信息成功",
data: { id, username }
};
});
app.use(router.routes());
// 5. JWT 错误处理
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
if (err.status === 401) {
ctx.status = 401;
ctx.body = { code: 401, msg: "token 无效或过期" };
} else {
throw err;
}
}
});
app.listen(port, () => {
console.log(`JWT 服务器启动:http://localhost:${port}`);
});
测试 JWT
步骤 1:登录获取 token
curl -X POST -d "username=admin&password=123456" http://localhost:8008/api/login
# 响应:{"code":200,"msg":"登录成功","data":{"token":"xxx.xxx.xxx"}}
步骤 2:携带 token 访问受保护接口
curl -H "Authorization: Bearer 你的token" http://localhost:8008/api/user/info
# 响应:{"code":200,"msg":"获取用户信息成功","data":{"id":1,"username":"admin"}}
步骤 3:token 无效 / 过期测试
携带错误 token 或过期 token 访问,会返回 {"code":401,"msg":"token 无效或过期"}。
总结
-
核心流程:Koa 开发的核心是「中间件 + 路由」,所有功能(跨域、上传、JWT)都通过中间件扩展;
-
关键依赖:
@koa/router(路由)、koa-body(POST / 上传)、@koa/cors(跨域)、koa-session(Session)、jsonwebtoken/koa-jwt(JWT); -
生产建议:
-
Session/JWT 密钥需随机生成并加密存储;
-
文件上传需限制大小和类型,防止恶意上传;
-
跨域需指定具体域名,而非
*; -
JWT 载荷不要存敏感数据,过期时间不宜过长。
-