Koa框架
介紹
Koa 是一個新的 web 框架,由 Express 原班人馬打造,致力于成為一個更小、更富有表現力、更健壯的 Web 框架。
Koa 解決了 Express 存在的一些問題,例如:
- 中間件嵌套回調(callback hell)
- 錯誤處理不統一
- 上下文信息分散
Koa 通過使用 async/await 和一個統一的 Context 上下文對象,使得代碼更簡潔、可讀性更高。
核心特點
特性 | 說明 |
---|---|
輕量無內置 | 不內置任何中間件(如路由、模板等),開發者按需選擇 |
全異步 | 支持?async/await ,默認異步中間件 |
中間件機制 | 洋蔥模型(onion model),控制流程清晰 |
易擴展 | 社區插件豐富,靈活搭配功能 |
Context 對象 | 封裝了原生 req/res,更方便操作請求和響應 |
Koa對比Express
比較點 | Koa | Express |
---|---|---|
中間件模型 | 洋蔥模型 | 線性模型(use 依次執行) |
異步支持 | 原生 async/await | 需要手動處理異步回調 |
內置中間件 | 無 | 內置很多(如 body-parser) |
上下文封裝 | 有(ctx ?封裝 req/res) | 無(直接使用 req, res) |
靈活性 | 高(插件式) | 較低(結構固定) |
請求和響應
請求別名以下訪問器和別名請求等效項:ctx.headerctx.headersctx.methodctx.method=ctx.urlctx.url=ctx.originalUrlctx.originctx.hrefctx.pathctx.path=ctx.queryctx.query=ctx.querystringctx.querystring=ctx.hostctx.hostnamectx.freshctx.stalectx.socketctx.protocolctx.securectx.ipctx.ipsctx.subdomainsctx.is()ctx.accepts()ctx.acceptsEncodings()ctx.acceptsCharsets()ctx.acceptsLanguages()ctx.get()
響應別名
以下訪問器和別名響應等效項:
ctx.body
ctx.body=
ctx.has()
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type=
ctx.type
ctx.vary()
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=
ctx.writable
路由
一、基本使用流程
1. 安裝依賴
npm install koa koa-router
2. 簡單示例
const Koa = require('koa');
const Router = require('koa-router');const app = new Koa();
const router = new Router(); // 創建路由實例// 定義路由:GET 請求 + 路徑 '/'
router.get('/', (ctx) => {ctx.body = 'Hello, Koa Router!'; // 設置響應體
});// 注冊路由到 Koa 應用
app.use(router.routes());
// 啟用路由中間件的 HTTP 方法驗證(如 405 Method Not Allowed)
app.use(router.allowedMethods());app.listen(3000, () => {console.log('Server running on http://localhost:3000');
});
二、核心概念與用法
1. 路由方法
koa-router 支持所有 HTTP 方法(GET、POST、PUT、DELETE 等),語法統一為:
router.方法名(路徑, 處理函數);
示例:
// GET 請求
router.get('/users', (ctx) => {ctx.body = ['用戶1', '用戶2'];
});// POST 請求(通常需解析請求體,可配合 koa-bodyparser)
router.post('/users', (ctx) => {const newUser = ctx.request.body; // 需要 koa-bodyparser 解析ctx.body = { message: '用戶創建成功', data: newUser };ctx.status = 201; // 設置狀態碼
});// 匹配所有方法
router.all('/test', (ctx) => {ctx.body = '支持所有 HTTP 方法';
});
2. 路由路徑
路徑可以是字符串、字符串模式或正則表達式:
// 精確匹配
router.get('/about', (ctx) => { ctx.body = '關于我們'; });// 帶參數的路徑(動態路由)
router.get('/users/:id', (ctx) => {const userId = ctx.params.id; // 獲取路徑參數ctx.body = `用戶 ID: ${userId}`;
});// 多參數
router.get('/users/:userId/posts/:postId', (ctx) => {console.log(ctx.params); // { userId: '123', postId: '456' }
});// 通配符匹配(? 匹配 0 或 1 個字符)
router.get('/user/?name', (ctx) => { ... });// 正則表達式(匹配以 /api 開頭的路徑)
router.get(/^\/api/, (ctx) => { ... });
3. 路由中間件
路由處理函數本質是 Koa 中間件,支持異步行多個中間件,并通過 next() 傳遞控制權:
// 日志中間件
const logMiddleware = async (ctx, next) => {console.log(`訪問路徑: ${ctx.path}`);await next(); // 執行下一個中間件
};// 權限行中間件:先日志,再處理響應
router.get('/users', logMiddleware, async (ctx) => {ctx.body = ['用戶1', '用戶2'];
});
4. 路由前綴
為一組組路由統一添加前綴,簡化路徑定義:
// 創建帶前綴的路由實例
const userRouter = new Router({ prefix: '/users' });// 實際路徑為 /users
userRouter.get('/', (ctx) => { ... });
// 實際路徑為 /users/123
userRouter.get('/:id', (ctx) => { ... });// 注冊到應用
app.use(userRouter.routes());
5. 嵌套路由
通過 use() 實現路由嵌套,適合大型應用拆分路由模塊:
// userRoutes.js(子路由)
const Router = require('koa-router');
const userRouter = new Router();userRouter.get('/', (ctx) => { ... });
userRouter.get('/:id', (ctx) => { ... });module.exports = userRouter;// 主文件
const Koa = require('koa');
const app = new Koa();
const userRouter = require('./userRoutes');// 嵌套路由:所有 userRouter 路由會被掛載到 /api 下
const apiRouter = new Router({ prefix: '/api' });
apiRouter.use('/users', userRouter.routes()); // 實際路徑:/api/usersapp.use(apiRouter.routes());
6. 響應處理
通過 ctx 對象操作請求和響應:
ctx.request:請求對象(包含 method、url、query、body 等)
ctx.response:響應對象(包含 body、status、headers 等)
簡寫:ctx.body 等價于 ctx.response.body,ctx.status 等價于 ctx.response.status
示例:
router.get('/query', (ctx) => {// 獲取查詢參數(?name=tom&age=18)console.log(ctx.query); // { name: 'tom', age: '18' }ctx.body = { query: ctx.query };
});
7. 路由重定向
使用 ctx.redirect() 實現重定向:
router.get('/old', (ctx) => {ctx.redirect('/new'); // 重定向到 /newctx.status = 301; // 可選:設置 301 永久重定向(默認 302)
});router.get('/new', (ctx) => {ctx.body = '新頁面';
});
三、allowedMethods 的作用
router.allowedMethods() 是一個中間件,用于處理不支持的 HTTP 方法,自動返回標準響應:
當請求方法不被支持時(如對 GET 路由發送 POST 請求),返回 405 Method Not Allowed 當請求的 HTTP 方法未在服務器實現時(如 PROPFIND),返回 501 Not Implemented
必須在 router.routes() 之后注冊:
app.use(router.routes());
app.use(router.allowedMethods()); // 放在 routes 后面
四、常見問題與最佳實踐
- 路由順序:Koa 路由按定義順序匹配,精確路徑應放在模糊路徑之前,避免被覆蓋:
// 正確:先精確匹配
router.get('/users/profile', (ctx) => { ... });
// 后模糊匹配
router.get('/users/:id', (ctx) => { ... });
路由模塊化:大型應用建議按功能拆分路由文件(如 userRoutes.js、postRoutes.js),再通過嵌套路由組合。
請求體解析:處理 POST、PUT 等請求時,需使用 koa-bodyparser 中間件解析請求體:
npm install koa-bodyparser
const bodyParser = require('koa-bodyparser');
app.use(bodyParser()); // 需在路由之前注冊
- 錯誤處理:在路由中間件中通過 try/catch 捕獲錯誤,并統一處理:
router.get('/users/:id', async (ctx) => {try {const user = await getUserById(ctx.params.id);if (!user) {ctx.status = 404;ctx.body = '用戶不存在';return;}ctx.body = user;} catch (err) {ctx.status = 500;ctx.body = '服務器錯誤';}
});
?