文章目录
- 
- [什么是 Koa.js?](#什么是 Koa.js?)
 - [为什么选择 Koa?](#为什么选择 Koa?)
 - 
- [Koa 与 Express 的对比](#Koa 与 Express 的对比)
 - [Koa 的优势](#Koa 的优势)
 
 - 环境准备与安装
 - 
- [1. 系统要求](#1. 系统要求)
 - [2. 创建 Koa 项目](#2. 创建 Koa 项目)
 - [3. 基础项目结构](#3. 基础项目结构)
 
 - [Koa 核心概念](#Koa 核心概念)
 - 
- [1. 应用程序 (Application)](#1. 应用程序 (Application))
 - [2. 上下文 (Context)](#2. 上下文 (Context))
 - [3. 请求 (Request) 和响应 (Response)](#3. 请求 (Request) 和响应 (Response))
 
 - 中间件详解
 - 
- [1. 洋葱模型](#1. 洋葱模型)
 - [2. 常用内置中间件](#2. 常用内置中间件)
 - [3. 自定义中间件](#3. 自定义中间件)
 
 - 路由处理
 - 
- [1. 使用 koa-router](#1. 使用 koa-router)
 - [2. 路由模块化](#2. 路由模块化)
 
 - 常用中间件
 - 
- [1. 第三方中间件安装](#1. 第三方中间件安装)
 - [2. 中间件配置示例](#2. 中间件配置示例)
 
 - 数据库集成
 - 
- [1. MySQL 集成](#1. MySQL 集成)
 - [2. 用户服务示例](#2. 用户服务示例)
 
 - 错误处理
 - 
- [1. 全局错误处理](#1. 全局错误处理)
 - [2. 自定义错误类](#2. 自定义错误类)
 
 - 认证与授权
 - 
- [1. JWT 认证](#1. JWT 认证)
 - [2. 密码加密](#2. 密码加密)
 - [3. 认证控制器](#3. 认证控制器)
 
 - 总结
 
 
什么是 Koa.js?
Koa.js 是一个由 Express 原班人马打造的下一代 Node.js Web 框架,旨在成为一个更小、更富有表现力、更健壮的 Web 框架。通过利用 async 函数,Koa 可以帮助你丢弃回调函数,并极大地增强错误处理的能力。
Koa 没有捆绑任何中间件,而是提供了一套优雅的方法来编写服务器端应用程序,让开发者可以自由地选择所需的中间件组件。
- Koa 是一个轻量级的 Node.js Web 框架,核心代码只有约 600 行
 - 采用洋葱模型中间件机制,执行顺序更加清晰
 - 完全基于 Promise 和 async/await,避免回调地狱
 - 具有更好的错误处理机制
 
为什么选择 Koa?
Koa 与 Express 的对比
| 特性 | Express | Koa | 
|---|---|---|
| 中间件机制 | 线性执行 | 洋葱模型 | 
| 异步处理 | 回调函数 | async/await | 
| 错误处理 | 回调错误 | Try/Catch | 
| 体积大小 | 相对较大 | 非常轻量 | 
| 学习曲线 | 平缓 | 需要 Promise 知识 | 
| 灵活性 | 内置功能多 | 高度可定制 | 
Koa 的优势
- 更现代的异步处理:基于 async/await,代码更清晰
 - 更好的错误处理:使用 try/catch 捕获错误
 - 轻量级设计:核心简洁,按需添加中间件
 - 洋葱模型:中间件执行顺序更可控
 - ES6+ 特性:充分利用现代 JavaScript 特性
 
环境准备与安装
1. 系统要求
- Node.js 版本 7.6.0 或更高(支持 async/await)
 - npm 或 yarn 包管理器
 
2. 创建 Koa 项目
            
            
              bash
              
              
            
          
          # 创建项目目录
mkdir koa-app
cd koa-app
# 初始化 package.json
pnpm init
# 安装 Koa
pnpm i koa
# 安装开发依赖(可选)
pnpm i -D nodemon
        3. 基础项目结构
koa-app/
├── package.json
├── app.js
├── src/
│   ├── controllers/
│   ├── middleware/
│   ├── routes/
│   ├── utils/
│   └── config/
├── public/
└── views/
        Koa 核心概念
1. 应用程序 (Application)
基础 Koa 应用:
            
            
              javascript
              
              
            
          
          // app.js
const Koa = require('koa');
const app = new Koa();
// 最简单的中间件
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});
// 响应中间件
app.use(async (ctx) => {
  ctx.body = 'Hello Koa!';
});
// 启动服务器
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});
        启动应用:
            
            
              bash
              
              
            
          
          node app.js
# 或使用 nodemon
npx nodemon app.js
        2. 上下文 (Context)
Context 对象包含了请求和响应的所有信息:
            
            
              javascript
              
              
            
          
          app.use(async (ctx) => {
  // 请求信息
  console.log('Method:', ctx.method);       // 请求方法
  console.log('URL:', ctx.url);             // 请求 URL
  console.log('Path:', ctx.path);           // 请求路径
  console.log('Query:', ctx.query);         // 查询参数
  console.log('Headers:', ctx.headers);     // 请求头
  
  // 响应控制
  ctx.status = 200;                         // 状态码
  ctx.type = 'application/json';            // 内容类型
  ctx.body = {                              // 响应体
    message: 'Hello Koa!',
    timestamp: new Date().toISOString()
  };
  
  // 设置响应头
  ctx.set('X-Custom-Header', 'custom-value');
});
        3. 请求 (Request) 和响应 (Response)
Request 对象:
            
            
              javascript
              
              
            
          
          app.use(async (ctx) => {
  const { request } = ctx;
  
  // 请求属性
  console.log('Method:', request.method);
  console.log('URL:', request.url);
  console.log('Original URL:', request.originalUrl);
  console.log('Origin:', request.origin);
  console.log('Path:', request.path);
  console.log('Query:', request.query);
  console.log('Querystring:', request.querystring);
  console.log('Host:', request.host);
  console.log('Hostname:', request.hostname);
  console.log('Fresh:', request.fresh);
  console.log('Stale:', request.stale);
  console.log('Protocol:', request.protocol);
  console.log('Secure:', request.secure);
  console.log('IP:', request.ip);
  console.log('IPs:', request.ips);
  console.log('Subdomains:', request.subdomains);
  
  // 请求头
  console.log('Headers:', request.headers);
  console.log('Content-Type:', request.type);
  console.log('Content-Length:', request.length);
  console.log('Accept:', request.accepts());
  
  // 获取特定头信息
  console.log('User-Agent:', request.get('User-Agent'));
});
        Response 对象:
            
            
              javascript
              
              
            
          
          app.use(async (ctx) => {
  const { response } = ctx;
  
  // 设置状态码
  response.status = 201;
  
  // 设置响应头
  response.set('X-Powered-By', 'Koa');
  response.set({
    'X-Custom-Header': 'value1',
    'X-Another-Header': 'value2'
  });
  
  // 设置内容类型
  response.type = 'application/json';
  response.type = 'text/html; charset=utf-8';
  
  // 设置响应体
  response.body = { message: 'Hello World' };
  response.body = 'Hello World';
  response.body = Buffer.from('Hello World');
  
  // 重定向
  response.redirect('/new-location');
  response.redirect('https://example.com');
  
  // 附件下载
  response.attachment('filename.txt');
  
  // 最后修改时间
  response.lastModified = new Date();
  
  // ETag
  response.etag = 'hash-string';
});
        中间件详解
1. 洋葱模型
Koa 中间件的执行顺序:
            
            
              javascript
              
              
            
          
          app.use(async (ctx, next) => {
  console.log('1. 开始');
  await next();
  console.log('6. 结束');
});
app.use(async (ctx, next) => {
  console.log('2. 进入');
  await next();
  console.log('5. 退出');
});
app.use(async (ctx, next) => {
  console.log('3. 核心处理');
  ctx.body = 'Hello World';
  console.log('4. 响应设置');
});
// 执行顺序: 1 → 2 → 3 → 4 → 5 → 6
        2. 常用内置中间件
            
            
              javascript
              
              
            
          
          const Koa = require('koa');
const app = new Koa();
// 错误处理中间件
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = {
      error: err.message,
      stack: process.env.NODE_ENV === 'development' ? err.stack : {}
    };
    ctx.app.emit('error', err, ctx);
  }
});
// 日志中间件
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
// 响应时间中间件
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});
// 静态文件服务(需要 koa-static)
// app.use(require('koa-static')('public'));
        3. 自定义中间件
认证中间件:
            
            
              javascript
              
              
            
          
          // middleware/auth.js
const auth = () => {
  return async (ctx, next) => {
    const token = ctx.headers.authorization;
    
    if (!token) {
      ctx.throw(401, 'No token provided');
    }
    
    try {
      // 验证 token(这里简化处理)
      const user = await verifyToken(token.replace('Bearer ', ''));
      ctx.state.user = user;
      await next();
    } catch (error) {
      ctx.throw(401, 'Invalid token');
    }
  };
};
async function verifyToken(token) {
  // 实际项目中会使用 JWT 等验证机制
  return { id: 1, username: 'admin' };
}
module.exports = auth;
        参数验证中间件:
            
            
              javascript
              
              
            
          
          // middleware/validator.js
const validate = (rules) => {
  return async (ctx, next) => {
    const errors = [];
    
    // 验证查询参数
    if (rules.query) {
      for (const [key, rule] of Object.entries(rules.query)) {
        const value = ctx.query[key];
        
        if (rule.required && (value === undefined || value === '')) {
          errors.push(`${key} is required`);
        }
        
        if (value && rule.type && typeof value !== rule.type) {
          errors.push(`${key} should be ${rule.type}`);
        }
      }
    }
    
    // 验证请求体
    if (rules.body) {
      for (const [key, rule] of Object.entries(rules.body)) {
        const value = ctx.request.body[key];
        
        if (rule.required && (value === undefined || value === '')) {
          errors.push(`${key} is required`);
        }
        
        if (value && rule.type && typeof value !== rule.type) {
          errors.push(`${key} should be ${rule.type}`);
        }
      }
    }
    
    if (errors.length > 0) {
      ctx.throw(400, `Validation failed: ${errors.join(', ')}`);
    }
    
    await next();
  };
};
module.exports = validate;
        路由处理
1. 使用 koa-router
安装:
            
            
              bash
              
              
            
          
          pnpm i koa-router
        基础路由:
            
            
              javascript
              
              
            
          
          const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 基本路由
router.get('/', async (ctx) => {
  ctx.body = 'Home Page';
});
router.get('/about', async (ctx) => {
  ctx.body = 'About Page';
});
// 路由参数
router.get('/users/:id', async (ctx) => {
  ctx.body = `User ID: ${ctx.params.id}`;
});
// 查询参数
router.get('/search', async (ctx) => {
  const { q, page = 1 } = ctx.query;
  ctx.body = {
    query: q,
    page: parseInt(page),
    results: [`Result 1 for ${q}`, `Result 2 for ${q}`]
  };
});
// POST 请求
router.post('/users', async (ctx) => {
  const userData = ctx.request.body;
  ctx.status = 201;
  ctx.body = { message: 'User created', id: Date.now() };
});
// PUT 请求
router.put('/users/:id', async (ctx) => {
  const userId = ctx.params.id;
  const userData = ctx.request.body;
  ctx.body = { message: `User ${userId} updated` };
});
// DELETE 请求
router.delete('/users/:id', async (ctx) => {
  const userId = ctx.params.id;
  ctx.status = 204;
});
// 注册路由
app.use(router.routes());
app.use(router.allowedMethods());
        2. 路由模块化
routes/users.js:
            
            
              javascript
              
              
            
          
          const Router = require('koa-router');
const router = new Router({ prefix: '/users' });
// 获取所有用户
router.get('/', async (ctx) => {
  ctx.body = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ];
});
// 获取单个用户
router.get('/:id', async (ctx) => {
  const userId = ctx.params.id;
  ctx.body = { id: userId, name: `User ${userId}` };
});
// 创建用户
router.post('/', async (ctx) => {
  const userData = ctx.request.body;
  ctx.status = 201;
  ctx.body = { message: 'User created', id: Date.now(), ...userData };
});
// 更新用户
router.put('/:id', async (ctx) => {
  const userId = ctx.params.id;
  const userData = ctx.request.body;
  ctx.body = { message: `User ${userId} updated`, ...userData };
});
// 删除用户
router.delete('/:id', async (ctx) => {
  const userId = ctx.params.id;
  ctx.status = 204;
});
module.exports = router;
        主应用文件:
            
            
              javascript
              
              
            
          
          const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const userRoutes = require('./routes/users');
const app = new Koa();
// 中间件
app.use(bodyParser());
// 路由
app.use(userRoutes.routes());
app.use(userRoutes.allowedMethods());
app.listen(3000);
        常用中间件
1. 第三方中间件安装
            
            
              bash
              
              
            
          
          # 请求体解析
pnpm i koa-bodyparser
# 静态文件服务
pnpm i koa-static
# 会话管理
pnpm i koa-session
# CORS 支持
pnpm i @koa/cors
# 速率限制
pnpm i koa-ratelimit
# 压缩
pnpm i koa-compress
# 安全头
pnpm i koa-helmet
# JWT 认证
pnpm i koa-jwt jsonwebtoken
        2. 中间件配置示例
            
            
              javascript
              
              
            
          
          const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const static = require('koa-static');
const cors = require('@koa/cors');
const compress = require('koa-compress');
const helmet = require('koa-helmet');
const session = require('koa-session');
const app = new Koa();
// 安全头
app.use(helmet());
// CORS
app.use(cors({
  origin: 'http://localhost:3000',
  credentials: true
}));
// 压缩
app.use(compress({
  threshold: 2048,
  gzip: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  deflate: {
    flush: require('zlib').constants.Z_SYNC_FLUSH
  },
  br: false
}));
// 会话配置
app.keys = ['your-session-secret'];
app.use(session({
  key: 'koa.sess',
  maxAge: 86400000,
  overwrite: true,
  httpOnly: true,
  signed: true,
  rolling: false,
  renew: false
}, app));
// 请求体解析
app.use(bodyParser({
  enableTypes: ['json', 'form', 'text'],
  formLimit: '10mb',
  jsonLimit: '10mb'
}));
// 静态文件服务
app.use(static('public'));
// 错误处理
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = {
      error: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    };
    ctx.app.emit('error', err, ctx);
  }
});
// 应用错误事件监听
app.on('error', (err, ctx) => {
  console.error('Server error:', err);
});
        数据库集成
1. MySQL 集成
安装依赖:
            
            
              bash
              
              
            
          
          pnpm i mysql2
        数据库配置:
            
            
              javascript
              
              
            
          
          // config/database.js
const mysql = require('mysql2/promise');
const dbConfig = {
  host: process.env.DB_HOST || 'localhost',
  port: process.env.DB_PORT || 3306,
  user: process.env.DB_USER || 'root',
  password: process.env.DB_PASSWORD || 'password',
  database: process.env.DB_NAME || 'koa_app',
  connectionLimit: 10,
  acquireTimeout: 60000,
  timeout: 60000,
};
const pool = mysql.createPool(dbConfig);
// 查询封装
const query = async (sql, params = []) => {
  const connection = await pool.getConnection();
  try {
    const [results] = await connection.execute(sql, params);
    return results;
  } finally {
    connection.release();
  }
};
module.exports = {
  pool,
  query
};
        2. 用户服务示例
            
            
              javascript
              
              
            
          
          // services/userService.js
const { query } = require('../config/database');
class UserService {
  // 获取所有用户
  async findAll() {
    const sql = 'SELECT * FROM users WHERE deleted_at IS NULL';
    return await query(sql);
  }
  // 根据ID查找用户
  async findById(id) {
    const sql = 'SELECT * FROM users WHERE id = ? AND deleted_at IS NULL';
    const users = await query(sql, [id]);
    return users[0] || null;
  }
  // 根据邮箱查找用户
  async findByEmail(email) {
    const sql = 'SELECT * FROM users WHERE email = ? AND deleted_at IS NULL';
    const users = await query(sql, [email]);
    return users[0] || null;
  }
  // 创建用户
  async create(userData) {
    const { username, email, password } = userData;
    const sql = `
      INSERT INTO users (username, email, password, created_at, updated_at)
      VALUES (?, ?, ?, NOW(), NOW())
    `;
    const result = await query(sql, [username, email, password]);
    return this.findById(result.insertId);
  }
  // 更新用户
  async update(id, userData) {
    const fields = [];
    const values = [];
    
    Object.keys(userData).forEach(key => {
      if (userData[key] !== undefined) {
        fields.push(`${key} = ?`);
        values.push(userData[key]);
      }
    });
    
    if (fields.length === 0) {
      return this.findById(id);
    }
    
    values.push(id);
    const sql = `
      UPDATE users 
      SET ${fields.join(', ')}, updated_at = NOW() 
      WHERE id = ? AND deleted_at IS NULL
    `;
    
    await query(sql, values);
    return this.findById(id);
  }
  // 删除用户(软删除)
  async delete(id) {
    const sql = 'UPDATE users SET deleted_at = NOW() WHERE id = ?';
    await query(sql, [id]);
  }
}
module.exports = new UserService();
        错误处理
1. 全局错误处理
            
            
              javascript
              
              
            
          
          // middleware/errorHandler.js
const errorHandler = () => {
  return async (ctx, next) => {
    try {
      await next();
      
      // 处理 404
      if (ctx.status === 404 && !ctx.body) {
        ctx.throw(404, 'Resource not found');
      }
    } catch (err) {
      ctx.status = err.status || 500;
      
      // 开发环境返回详细错误信息
      if (process.env.NODE_ENV === 'development') {
        ctx.body = {
          error: err.message,
          stack: err.stack,
          status: ctx.status
        };
      } else {
        // 生产环境返回通用错误信息
        ctx.body = {
          error: ctx.status === 500 ? 'Internal Server Error' : err.message,
          status: ctx.status
        };
      }
      
      // 触发错误事件
      ctx.app.emit('error', err, ctx);
    }
  };
};
module.exports = errorHandler;
        2. 自定义错误类
            
            
              javascript
              
              
            
          
          // utils/AppError.js
class AppError extends Error {
  constructor(message, status = 500, code = null) {
    super(message);
    this.name = this.constructor.name;
    this.status = status;
    this.code = code;
    Error.captureStackTrace(this, this.constructor);
  }
}
class ValidationError extends AppError {
  constructor(message = 'Validation failed') {
    super(message, 400, 'VALIDATION_ERROR');
  }
}
class NotFoundError extends AppError {
  constructor(message = 'Resource not found') {
    super(message, 404, 'NOT_FOUND');
  }
}
class UnauthorizedError extends AppError {
  constructor(message = 'Unauthorized') {
    super(message, 401, 'UNAUTHORIZED');
  }
}
class ForbiddenError extends AppError {
  constructor(message = 'Forbidden') {
    super(message, 403, 'FORBIDDEN');
  }
}
module.exports = {
  AppError,
  ValidationError,
  NotFoundError,
  UnauthorizedError,
  ForbiddenError
};
        认证与授权
1. JWT 认证
            
            
              javascript
              
              
            
          
          // middleware/jwtAuth.js
const jwt = require('jsonwebtoken');
const { UnauthorizedError } = require('../utils/AppError');
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
const jwtAuth = () => {
  return async (ctx, next) => {
    const authHeader = ctx.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new UnauthorizedError('No token provided');
    }
    
    const token = authHeader.slice(7);
    
    try {
      const decoded = jwt.verify(token, JWT_SECRET);
      ctx.state.user = decoded;
      await next();
    } catch (error) {
      throw new UnauthorizedError('Invalid token');
    }
  };
};
// 生成 JWT Token
const generateToken = (payload, options = {}) => {
  return jwt.sign(payload, JWT_SECRET, {
    expiresIn: '7d',
    ...options
  });
};
module.exports = {
  jwtAuth,
  generateToken
};
        2. 密码加密
            
            
              javascript
              
              
            
          
          // utils/password.js
const bcrypt = require('bcryptjs');
const saltRounds = 12;
// 加密密码
const hashPassword = async (password) => {
  return await bcrypt.hash(password, saltRounds);
};
// 验证密码
const verifyPassword = async (password, hashedPassword) => {
  return await bcrypt.compare(password, hashedPassword);
};
module.exports = {
  hashPassword,
  verifyPassword
};
        3. 认证控制器
            
            
              javascript
              
              
            
          
          // controllers/authController.js
const userService = require('../services/userService');
const { generateToken } = require('../middleware/jwtAuth');
const { hashPassword, verifyPassword } = require('../utils/password');
const { ValidationError, UnauthorizedError } = require('../utils/AppError');
class AuthController {
  // 用户注册
  async register(ctx) {
    const { username, email, password } = ctx.request.body;
    
    // 验证输入
    if (!username || !email || !password) {
      throw new ValidationError('Username, email and password are required');
    }
    
    // 检查用户是否已存在
    const existingUser = await userService.findByEmail(email);
    if (existingUser) {
      throw new ValidationError('User already exists');
    }
    
    // 加密密码
    const hashedPassword = await hashPassword(password);
    
    // 创建用户
    const user = await userService.create({
      username,
      email,
      password: hashedPassword
    });
    
    // 生成 token
    const token = generateToken({
      userId: user.id,
      email: user.email
    });
    
    ctx.status = 201;
    ctx.body = {
      message: 'User registered successfully',
      user: {
        id: user.id,
        username: user.username,
        email: user.email
      },
      token
    };
  }
  
  // 用户登录
  async login(ctx) {
    const { email, password } = ctx.request.body;
    
    if (!email || !password) {
      throw new ValidationError('Email and password are required');
    }
    
    // 查找用户
    const user = await userService.findByEmail(email);
    if (!user) {
      throw new UnauthorizedError('Invalid credentials');
    }
    
    // 验证密码
    const isValidPassword = await verifyPassword(password, user.password);
    if (!isValidPassword) {
      throw new UnauthorizedError('Invalid credentials');
    }
    
    // 生成 token
    const token = generateToken({
      userId: user.id,
      email: user.email
    });
    
    ctx.body = {
      message: 'Login successful',
      user: {
        id: user.id,
        username: user.username,
        email: user.email
      },
      token
    };
  }
  
  // 获取当前用户信息
  async getCurrentUser(ctx) {
    const user = await userService.findById(ctx.state.user.userId);
    
    if (!user) {
      throw new UnauthorizedError('User not found');
    }
    
    ctx.body = {
      user: {
        id: user.id,
        username: user.username,
        email: user.email
      }
    };
  }
}
module.exports = new AuthController();
        总结
Koa.js 作为一个现代、轻量级的 Node.js Web 框架,通过其洋葱模型中间件机制和基于 async/await 的异步处理,为开发者提供了更加优雅和高效的开发体验。相比 Express,Koa 在错误处理、中间件控制和代码可读性方面都有显著提升。
本文详细介绍了 Koa 的核心概念、中间件机制、路由处理、数据库集成、认证授权以及实际项目开发的全过程。掌握这些知识后,你将能够使用 Koa 构建出高性能、可维护的 Web 应用程序。
Koa 的轻量级设计和高度可定制性使其成为构建现代 Web 应用的理想选择。随着对框架的深入理解,你可以进一步探索 Koa 与各种数据库、缓存、消息队列等技术的集成,构建出更加复杂和强大的应用系统。