首页 / Node.js 教程 / koa 路由与请求处理

Node.js 教程

koa 路由与请求处理

本教程共 76 篇 · 第 45 篇 · 更新于 2026-07-25 · 约 4 分钟阅读

Node.jskoa路由koa-router请求处理

45. koa 路由与请求处理

本节目标:koa-router 路由、请求解析和响应处理。

上一章的 koa 应用不管访问什么 URL 都返回同样的内容,这显然不够用。本章我们引入 @koa/router 做路由分发,再用 @koa/bodyparser 解析请求体,最后把控制器拆到独立目录,让项目结构清晰起来。

路由基础

koa 核心没有内置路由,需要安装 @koa/router

npm install @koa/router

基本用法:

import Koa from 'koa';
import Router from '@koa/router';

const app = new Koa();
const router = new Router();

router.get('/', async (ctx) => {
  ctx.body = '首页';
});

router.get('/hello/:name', async (ctx) => {
  ctx.body = `你好,${ctx.params.name}`;
});

router.post('/login', async (ctx) => {
  ctx.body = '登录成功';
});

app.use(router.routes());
app.listen(3000);

路由参数通过 ctx.params 获取,查询字符串通过 ctx.query 获取:

// GET /search?q=node
router.get('/search', async (ctx) => {
  ctx.body = `搜索:${ctx.query.q}`;
});
Tip

ctx.query 返回的是已经解析好的对象,不用自己调 URLSearchParams,比原生 http 模块省事多了。

解析请求体

POST 请求发过来的表单或 JSON,koa 默认不会自动解析。装 @koa/bodyparser

npm install @koa/bodyparser
import { bodyParser } from '@koa/bodyparser';

app.use(bodyParser());

router.post('/login', async (ctx) => {
  const { username, password } = ctx.request.body;
  ctx.body = { username, msg: '收到' };
});

注意:bodyParser 必须放在 router.routes() 之前注册,否则请求走到路由时 body 还没解析好。

用 curl 测试:

curl -X POST http://localhost:3000/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"123456"}'

中间件的顺序艺术

koa 中间件的注册顺序就是执行顺序。一个典型的 Web 应用通常按这个顺序排列:

import Koa from 'koa';
import { bodyParser } from '@koa/bodyparser';
import Router from '@koa/router';

const app = new Koa();

// 1. 日志与计时
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  console.log(`${ctx.method} ${ctx.url} - ${ctx.status} - ${Date.now() - start}ms`);
});

// 2. 解析请求体
app.use(bodyParser());

// 3. 路由
const router = new Router();
router.get('/', async (ctx) => { ctx.body = 'home'; });
app.use(router.routes());

app.listen(3000);

想象一下流水线上的工人:第一个负责打卡计时,第二个负责拆包裹,第三个负责分拣货物。顺序错了,包裹还没拆就分拣,肯定要出错。

拆分控制器

路由一多,全堆在一个文件里会爆炸。我们按功能拆到 controllers 目录。

controllers/user.mjs

export async function list(ctx) {
  ctx.body = [{ id: 1, name: 'Alice' }];
}

export async function detail(ctx) {
  ctx.body = { id: ctx.params.id, name: 'Alice' };
}

app.mjs

import Router from '@koa/router';
import * as userController from './controllers/user.mjs';

const router = new Router();
router.get('/users', userController.list);
router.get('/users/:id', userController.detail);

路由前缀与嵌套

给一组 API 加统一前缀:

const apiRouter = new Router({ prefix: '/api/v1' });

apiRouter.get('/users', async (ctx) => {
  ctx.body = { users: [] };
});

app.use(apiRouter.routes());

这样用户列表的实际路径就是 /api/v1/users

允许的方法

router.routes() 默认只响应对应 HTTP 方法,其他方法会返回 405。如果你想让不匹配的路由直接走 404,可以加 router.allowedMethods()

app.use(router.routes());
app.use(router.allowedMethods());

它会自动处理 OPTIONS 预检请求,并给不支持的 HTTP 方法返回 405,附带 Allow 响应头告诉你支持哪些方法。