'use strict'; // 迷你路由(零依赖): // const app = createRouter(); // app.get('/api/job/:id', handler); // :段 提取参数 // app.any('/api/reports/status', h); // 不限方法(保持旧行为) // app.fallback(serveStatic); // 未命中路由时兜底 // http.createServer((req, res) => app.dispatch(req, res)); // 处理器签名:async ({ req, res, pathname, query, params }),异常由错误边界统一响应。 const { errorBoundary } = require('./middleware.js'); const { sendJson } = require('./util.js'); function toSegments(pattern) { return pattern.split('/').filter(Boolean) .map((seg) => (seg.startsWith(':') ? { name: seg.slice(1) } : seg)); } function createRouter() { const routes = []; let fallbackHandler = null; function add(method, pattern, handler) { routes.push({ method, segments: toSegments(pattern), handler: errorBoundary(handler) }); } return { get: (p, h) => add('GET', p, h), post: (p, h) => add('POST', p, h), del: (p, h) => add('DELETE', p, h), any: (p, h) => add('*', p, h), fallback: (fn) => { fallbackHandler = fn; }, async dispatch(req, res) { let pathname; let searchParams; try { const parsedUrl = new URL(req.url, `http://${req.headers.host}`); pathname = parsedUrl.pathname; searchParams = parsedUrl.searchParams; } catch { res.writeHead(400); return res.end('bad url'); } const query = Object.fromEntries(searchParams); const parts = pathname.split('/').filter(Boolean); let pathMatched = false; for (const route of routes) { if (route.segments.length !== parts.length) continue; const params = {}; let ok = true; for (let i = 0; i < parts.length; i++) { const seg = route.segments[i]; if (typeof seg === 'string') { if (seg !== parts[i]) { ok = false; break; } } else { params[seg.name] = parts[i]; } } if (!ok) continue; pathMatched = true; if (route.method !== '*' && route.method !== req.method) continue; return route.handler({ req, res, pathname, query, params }); } if (pathMatched) return sendJson(res, 405, { error: 'Method Not Allowed' }); if (fallbackHandler) return fallbackHandler(pathname, res); res.writeHead(404); res.end('Not found'); }, }; } module.exports = { createRouter };