[关闭]
@Dale-Lin 2020-09-27T21:39:09.000000Z 字数 2748 阅读 584

Connect 源码分析

Node.js实战


createServer 通过闭包返回增强的 app 对象:

  1. function createServer() {
  2. function app(req, res, next) {
  3. app.handle(req, res, next)
  4. }
  5. merge(app, proto)
  6. merge(app, EventEmitter.prototype)
  7. app.route = '/'
  8. app.stack = []
  9. return app
  10. }

proto 实现了 app 的核心方法 usehandle

  1. // 这里的 handle 是中间件
  2. proto.use = function use(route, fn) {
  3. // 获取挂载路径和中间件
  4. var handle = fn;
  5. var path = route;
  6. if (typeof route !== 'string') {
  7. handle = route;
  8. path = '/'
  9. }
  10. // 传入的是另一个 app 类的情况
  11. if (typeof handle.handle === 'function') {
  12. // 外层挂载
  13. var server = handle;
  14. server.route = path;
  15. // 将外层 app 的 handle 参传给内层
  16. handle = function (req, res, next) {
  17. server.handle(req, res, next);
  18. }
  19. }
  20. // 包装原生的 http.Servers
  21. if (handle instanceof http.Server) {
  22. handle = handle.listeners('request')[0];
  23. }
  24. // 除去挂载路径最后的 '/'
  25. if (path[path.length - 1] === '/') {
  26. path = path.slice(0, -1);
  27. }
  28. // 挂载中间件
  29. this.stack.push({ route: path, handle: handle });
  30. return this;
  31. }

handle 方法处理服务器请求,将它们传给中间件队列:

  1. proto.handle = function handle(req, res, out) {
  2. var stack = this.stack;
  3. var index = 0; // 中间件队列指针
  4. var photohost = getProtohost(req.url) || ''; // origin
  5. var removed = ''; // 删除的挂载路径
  6. var slashAdded = false;
  7. // 最后处理
  8. var done = out || finalhandler(req, res, {
  9. env: env,
  10. onerror: logerror,
  11. });
  12. // 保存原始 req.url
  13. req.originalUrl = req.originalUrl || req.url;
  14. // next 调用下一个中间件
  15. function next(err) {
  16. // 如果手动添加了 '/'
  17. if (slashAdded) {
  18. req.url = req.url.substr(1);
  19. slashAdded = false;
  20. }
  21. // 加上上一个中间件删除了的挂载路径
  22. if (removed.length !== 0) {
  23. req.url = protohost + removed + req.url.substr(protohost.length);
  24. removed = '';
  25. }
  26. // 下个中间件
  27. var layer = stack[index++]
  28. // 中间件队列结束
  29. if (!layer) {
  30. defer(done, err);
  31. return;
  32. }
  33. // 路由解析
  34. var path = parseUrl(req).pathname || '/';
  35. var route = layer.route;
  36. // req 路径和挂载路径不能匹配
  37. if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
  38. return next(err)
  39. }
  40. // req 路径是 route 路径的子目录
  41. // 但没有紧接 "/" 或 "." 或结束
  42. var c = path.length > route.length && path[route.length];
  43. if (c && c !== '/' && c !== '.') {
  44. return next(err)
  45. }
  46. // 删除 req 路径前匹配该 route 的路径
  47. if (route.length !== 0 && route !== '/') {
  48. removed = route;
  49. req.url = protohost + req.url.substr(photohost.length + removed.length);
  50. // 保证以 "/" 开头
  51. if (!protohost && req.url[0] !== '/') {
  52. req.url = '/' + req.url;
  53. slashAdded = true;
  54. }
  55. }
  56. // 调用当前层的中间件
  57. call(layer.handle, route, err, req, res, next);
  58. }
  59. // 调用默认的 out
  60. next();
  61. }
  62. // 默认使用 http
  63. proto.listen = function listen() {
  64. var server = http.createServer(this);
  65. return server.listen.apply(server, arguments);
  66. }

call 函数是决定调用或跳过中间件的地方:

  1. function call(handle, route, err, req, res, next) {
  2. var arity = handle.length; // 中间件接受的参数的个数
  3. var error = err;
  4. var hasError = Boolean(err);
  5. try {
  6. if (hasError && arity === 4) {
  7. // 有错误,且是错误处理中间件
  8. // 调用错误处理中间件
  9. handle(err, req, res, next);
  10. return;
  11. } else if (!hasError && arity < 4) {
  12. // 无错误,且当前不是错误处理中间件
  13. // 调用当前中间件
  14. handle(req, res, next);
  15. return;
  16. }
  17. } catch (e) {
  18. error = e;
  19. }
  20. // 跳过当前中间件
  21. next(error)
  22. }

辅助函数和对象:

  1. // 获取链接的协议和 host
  2. function getProtohost(url) {
  3. if (url.length === 0 || url[0] === '/') {
  4. return undefined;
  5. }
  6. var fqdnIndex = url.indexOf('://');
  7. return fqdnIndex !== -1 && url.lastIndexOf('?', fqdnIndex) === -1
  8. ? url.substr(0, url.indexOf('/', 3 + fqdnIndex) === -1
  9. : undefined;
  10. }
  11. // 打印错误
  12. function logerror(err) {
  13. if (env !== 'test') console.error(err.stack || err.toString());
  14. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注