express

This section explores five issues with the Express core commonly used in Node

1. What exactly does calling Express () create ❓

Open express Core’s lib/express.js and you’ll find this code

exports = module.exports = createApplication;
function createApplication() {
  var app = function(req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true.enumerable: true.writable: true.value: app }
  })

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true.enumerable: true.writable: true.value: app }
  })

  app.init();
  return app;
}
Copy the code

It is clear that the essence of calling Express () is a createApplication function

2. What does app.listen() do ❓

Here you may have two questions:

2.1 Isn’t app a function? Why can ❓ be calledlisten

App is a function, but it’s also an object, so you can call Listen

2.2 Listen is not found in createApplication

The app can just call listen and there’s a very important piece of processing here that says:

mixin(app, proto, false); // The bottom layer is mixed
Copy the code

In application.js, it calls http.createserver (this). Listen to pass all the parameters; On the basis of:

app.listen = function listen() {
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};

Copy the code

3. What happens inside app. Use ❓

Open application.js generates a global router in the app.use = function use() function, Var FNS = flatten(slice.call(arguments, offset)) The router. Use () function is then essentially called on line 228 after fetching each function via ferns.foreach ()

router.use(path, function mounted_app(req, res, next) {
      var orig = req.app;
      fn.handle(req, res, function (err) {
        setPrototypeOf(req, orig.request)
        setPrototypeOf(res, orig.response)
        next(err);
      });
    });
Copy the code

Skip to lib/router/index.js to find out. Proto.use = function Use (fn) = function use(fn) = new Layer(), and then push the generated Layer Layer onto the stack. The stack is an array of stacks originally bound to the router prototype. Our layer callback, this.stack.push(layer), where this is essentially the Router object.

4. How is the middleware called back ❓ when the user sends the request

Router. Handle (req, res, done); router.handle(req, res, done); Get our stack from self.stack in proto.handle() and call next() for the first time:

  // idx = 0 ; // init 
  function next(err) {
    var layerError = err === 'route'
      ? null
      : err;

    // remove added slash
    if (slashAdded) {
      req.url = req.url.substr(1);
      slashAdded = false;
    }

    // restore altered req.url
    if(removed.length ! = =0) {
      req.baseUrl = parentUrl;
      req.url = protohost + removed + req.url.substr(protohost.length);
      removed = ' ';
    }

    // signal to exit router
    if (layerError === 'router') {
      setImmediate(done, null)
      return
    }

    // no more matching layers
    if (idx >= stack.length) {
      setImmediate(done, layerError);
      return;
    }

    // get pathname of request
    var path = getPathname(req);

    if (path == null) {
      return done(layerError);
    }

    // find next matching layer
    var layer;
    var match;
    var route;

    while(match ! = =true && idx < stack.length) {
      layer = stack[idx++];
      match = matchLayer(layer, path);
      route = layer.route;

      if (typeofmatch ! = ='boolean') {
        // hold on to layerError
        layerError = layerError || match;
      }

      if(match ! = =true) {
        continue;
      }

      if(! route) {// process non-route handlers normally
        continue;
      }

      if (layerError) {
        // routes do not match with a pending error
        match = false;
        continue;
      }

      var method = req.method;
      var has_method = route._handles_method(method);

      // build up automatic options response
      if(! has_method && method ==='OPTIONS') {
        appendMethods(options, route._options());
      }

      // don't even bother matching route
      if(! has_method && method ! = ='HEAD') {
        match = false;
        continue; }}// no match
    if(match ! = =true) {
      return done(layerError);
    }

    // store route for dispatch on change
    if (route) {
      req.route = route;
    }

    // Capture one-time layer values
    req.params = self.mergeParams
      ? mergeParams(layer.params, parentParams)
      : layer.params;
    var layerPath = layer.path;

    // this should be done for the layer
    self.process_params(layer, paramcalled, req, res, function (err) {
      if (err) {
        return next(layerError || err);
      }

      if (route) {
        return layer.handle_request(req, res, next);
      }

      trim_prefix(layer, layerError, layerPath, path);
    });
  }
Copy the code

The layer handle_request() function is called. What does this function handle? And then we look down. What he’s doing is essentially calling the core FN (REq, RES, next); What is fn? Fn is the handle taken out of the layer

5. Why will next automatically execute the next middleware ❓

Understanding the process of the fourth question, when we register the middleware ourselves and call next() framework, the bottom layer will come to Next () to match the next stack for layer execution