router.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. 'use strict';
  2. // 迷你路由(零依赖):
  3. // const app = createRouter();
  4. // app.get('/api/job/:id', handler); // :段 提取参数
  5. // app.any('/api/reports/status', h); // 不限方法(保持旧行为)
  6. // app.fallback(serveStatic); // 未命中路由时兜底
  7. // http.createServer((req, res) => app.dispatch(req, res));
  8. // 处理器签名:async ({ req, res, pathname, query, params }),异常由错误边界统一响应。
  9. const { errorBoundary } = require('./middleware.js');
  10. const { sendJson } = require('./util.js');
  11. function toSegments(pattern) {
  12. return pattern.split('/').filter(Boolean)
  13. .map((seg) => (seg.startsWith(':') ? { name: seg.slice(1) } : seg));
  14. }
  15. function createRouter() {
  16. const routes = [];
  17. let fallbackHandler = null;
  18. function add(method, pattern, handler) {
  19. routes.push({ method, segments: toSegments(pattern), handler: errorBoundary(handler) });
  20. }
  21. return {
  22. get: (p, h) => add('GET', p, h),
  23. post: (p, h) => add('POST', p, h),
  24. del: (p, h) => add('DELETE', p, h),
  25. any: (p, h) => add('*', p, h),
  26. fallback: (fn) => { fallbackHandler = fn; },
  27. async dispatch(req, res) {
  28. let pathname;
  29. let searchParams;
  30. try {
  31. const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
  32. pathname = parsedUrl.pathname;
  33. searchParams = parsedUrl.searchParams;
  34. } catch {
  35. res.writeHead(400);
  36. return res.end('bad url');
  37. }
  38. const query = Object.fromEntries(searchParams);
  39. const parts = pathname.split('/').filter(Boolean);
  40. let pathMatched = false;
  41. for (const route of routes) {
  42. if (route.segments.length !== parts.length) continue;
  43. const params = {};
  44. let ok = true;
  45. for (let i = 0; i < parts.length; i++) {
  46. const seg = route.segments[i];
  47. if (typeof seg === 'string') {
  48. if (seg !== parts[i]) { ok = false; break; }
  49. } else {
  50. params[seg.name] = parts[i];
  51. }
  52. }
  53. if (!ok) continue;
  54. pathMatched = true;
  55. if (route.method !== '*' && route.method !== req.method) continue;
  56. return route.handler({ req, res, pathname, query, params });
  57. }
  58. if (pathMatched) return sendJson(res, 405, { error: 'Method Not Allowed' });
  59. if (fallbackHandler) return fallbackHandler(pathname, res);
  60. res.writeHead(404);
  61. res.end('Not found');
  62. },
  63. };
  64. }
  65. module.exports = { createRouter };