Prepend content

Node makes it easy and quick to start Http services:

const http = require("http");

// Create an HTTP service
const server = http.createServer((req, res) = > {
  res.end("hello Node");
});
// Listen on the port
server.listen(3000.() = > {
  console.log("Services started");
});
Copy the code

The Node service will start, and the browser will return Hello Node if it visits http://127.0.0.1:3000, but there is no route yet, so you’ll find that either http://127.0.0.1:3000/hi or any other path will return the same result. Therefore, Node server framework Express provides routing, middleware, request processing and other functions to help us provide services efficiently.

Starting the Express Service

Let’s use an example to understand how Express starts a service

const express = require("express");
const app = express();

// Start the Express service
app.listen("3000".() = > {
  console.log("Service already running on port 3000");
});
Copy the code

  • Express is a function. Executing Express returns an app function, which is used as a handler for http.createServer

  • Call app.listen to enable the Node service, which is essentially enabled through http.createserver (app)

  • Next can control the next call to handle or middleware and hand over the specific logic to App. handle. In fact, this is a very common proxy mode in Express. The app provides a unique entry point for all requests in the Express service

Registered routing

const express = require("express");
const app = express();

app.get("/".(req, res, next) = > {
  res.end("hello");
});

// Start the Express service
app.listen("3000".() = > {
  console.log("Service already running on port 3000");
});
Copy the code

Express only needs to call app.get to complete the route registration of GET request method. The internal execution process of route registration in Express involves the four most important modules: APP, Router, Route and layer.

  • The APP layer is the top-level design in Express and holds a Router routing object
  • The Router layer stores all routing information, and the internal layer stores it in its own stack
  • There are three main types of layer in Express (which are abstracted into the Layer class uniformly) :
    • route layer: Holds the route and saves it inrouter,
    • middleware layer: does not hold route, only correspondinghandleFunction, saved inrouter,
    • handle layer: Holds the final routehandleAnd stored in therouteIn the

App. get route registration process after we understand the responsibilities of each module in Express, app.get route registration process looks like this:

  1. App. get calls the router’s route method, which generates onerouteAnd aroute layerAnd letroute layerholdroute.routeprovideroute layeradispatchMethod as ahandleAnd finally press inrouterThe stack
  2. All of thehandleFunction is passed to the newly generatedroute, route.get is generatedhandle layerSave the corresponding Handle and store it on its own stack

Again through the source code to see express concrete implementation:

// application.js

// methods are a collection of HTTP methods defined in app.get, app.post, etc
methods.forEach(function(method){
  app[method] = function(path){
    if (method === 'get' && arguments.length === 1) {
      // app.get(setting)
      return this.set(path);
    }

    // Lazily initializes the Router object
    this.lazyrouter();

    // Perform the first step, executing the route method of the router
    var route = this._router.route(path);
    // Perform the second step, calling the newly created route.get method and passing the Handle
    route[method].apply(route, slice.call(arguments.1));
    return this;
  };
});


// router.js
proto.route = function route(path) {
  // Generate a new route
  var route = new Route(path);

  // Generate a Route Layer
  var layer = new Layer(path, {
    sensitive: this.caseSensitive,
    strict: this.strict,
    end: true
  }, route.dispatch.bind(route));

  // Let layer hold route
  layer.route = route;

  // Push layer onto the stack
  this.stack.push(layer);
  
  // Return the route object
  return route;
};

// route.js
methods.forEach(function(method){
  Route.prototype[method] = function(){
    // Flattens the array data structure
    var handles = flatten(slice.call(arguments));

    // Iterate over all handlers
    for (var i = 0; i < handles.length; i++) {
      var handle = handles[i];

      if (typeofhandle ! = ='function') {
        var type = toString.call(handle);
        var msg = 'Route.' + method + '() requires a callback function but got a ' + type
        throw new Error(msg);
      }

      // Create a Handle Layer. Each Handle layer holds a handle function
      var layer = Layer('/', {}, handle);
      layer.method = method;

      this.methods[method] = true;
      // The Handle Layer is pushed onto the route stack
      this.stack.push(layer);
    }

    return this;
  };
});
Copy the code

Hit the routing

Since we registered a route through app.get, the browser will respond to http://127.0.0.1:3000/, but not to any other address, indicating that the route is already working.

The routing flow now looks like this:

  1. As we know from the previous section, all requests to express will go through the serviceappIn the logic of,appexecuteapp.handleMethod,app.handleAnd to deal with the specific logicrouter.handleIs equivalent to forwarding the request to the Router layer for processing
  2. The request reaches the Router layer,router.handleYou go over yourselfstackAll of thelayer(Route layer and Middleware layer, in this case route Layer), and based onThe url addresses currently requested match the route path saved during registrationIs executed when a condition hitslayerthehandle_requestMethod, which is bound to theroute layerOf the corresponding routedispatchmethods
  3. route.dispatchThe logical androuter.handleSimilarly, traversal executes itselfstackIn thehandle layer, the implementation of thehandle_requestMethod, which is bound when the registered route is performedhandleFunction. Here Express will call the next onelayer.handle_requestGive it to the developer, if there are more than onehandle layer, developers can display the functions provided by Express (typically next) to implement eachhandleLogic of invocation

The middleware

Middleware Registration

Middleware is an important module of the Web server framework. The server often gives the same logic in the system to the middleware to complete, so as to realize authentication, logging, error handling, data conversion and other functions. Middleware in Express is a function, and we can use app.use to add a middleware.

const express = require("express");
const app = express();

app.use(function(req, res, next) {
  console.log('Current requested address :', req.url);
  // Note that next must be explicitly called otherwise the request/will not be matched
  next()
})

app.get("/".(req, res, next) = > {
  res.end("hello");
});

// Start the Express service
app.listen("3000".() = > {
  console.log("Service already running on port 3000");
});
Copy the code

We created a middleware that prints the request path. The internal flow of the Express registry middleware is as follows:

  1. app.useWill allhandlesThe handler is passed torouter.usemethods
  2. router.useIt’s going to go through all of themhandlesEach Handle creates onemiddleware layer, save it inmiddleware layerOn, androute layerThe difference is that this layer does not holdrouteAnd finally press it inrouterThe stack

Middleware hit

Use (path,… FNS), without path, it is a global middleware that can respond to all requests, because the registration of middleware is similar to that of route, and the matching process is the same as that of route. They both search for the corresponding layer according to path and execute its bound Handle function.

  1. app.handleGive the processing logic torouter.handle
  2. router.handlethroughpathFind the correspondingmiddleware layerSince we are registering global middleware in the example, this will be hitmiddleware layerAnd pull it out to perform binding on thelayerOn thehandle_requestmethods

Path matching

Express supports re as a path to register routes. You can use the following methods:

  • app.get('some? path', handle)// String is regular
  • app.get('somepath/:id', handle)// The path takes parameters
  • app.get(/somepath/, handle)// Regular expression paths are supported

These features benefit from path-to-Regexp support, where routing hits in Express are based on the re saved at registration time.

conclusion

Express designs app as a top-level structure, exposing app as an interface layer for developers to use, and internally handing the specific processing logic of app’s use, GET, and Handle methods to router, which is equivalent to app as an outward-exposed agent. Meanwhile, Express uses the while-interruptible route matching method to allow developers to use next to control the call of handle function, and developers need to pay attention to the order of each other when defining routes or middleware.