directory

  • An overview of the
  • Hello world example
  • Operation principle
  • Multiple routes multiple callbacks and middleware

An overview of the

Express is a fast, open and minimalist Web development framework based on node. js platform. It mainly has routing, middleware, template engine, error handling and other functions

Hello world example

Add 1.helloWorld.js to the test folder

var express = require('express');
var app = express();

app.get('/'.function (req, res) {
    res.end('Hello World! ');
});

var server = app.listen(3000, function () {
    console.log('Example app listening at 3000');
});
Copy the code

Run 1. Helloworls. Js

node 1.helloworls.js
Copy the code

The code above launches a website on port 3000 of the machine that says Hello World.

Operation principle

Now create a new lib folder and write your own Express library to see how it works

YUAN-EXPRESS
|
|
| - lib
|   | - application.js Wrap the app layer
|   | - express.js  # frame entry
|
| - test
|   | - 1.helloworld.js
|

Copy the code

express.js

const Application = require('./application');
function createApplicaton() {
    return new Application();
}
module.exports = createApplicaton;
Copy the code

Objective: To implement app.get and app.listen methods in application. Js

Actions: Construct Appliaction and add get and listen methods to the prototype

application.js

const http = require('http')
const url = require('url')

let router = [{
    path:"*",
    method:"*",
    handler(req,res){
        res.end(`Cannot ${req.method}_${req.url}`)}}]function Application() {

}

Application.prototype.get = functionRouter.push ({path, method:) {// Add the get method to the Application prototype.'get',
        handler
    })
}

Application.prototype.listen = function() {// Add the listen method to the Application prototype and execute the corresponding handler methodlet self = this
    const server = http.createServer(function (req,res) {
        let { pathname } = url.parse(req.url,true)
        for(var i = 1; i<router.length; i++){let { path,method,handler } = router[i]
            if (pathname == path && req.method.toLocaleLowerCase() == method){
                returnhandler(req,res) } } router[0].handler(req,res) }) server.listen(... arguments) } module.exports = ApplicationCopy the code

The Express framework is built on top of the HTTP module built into Node.js.

The key to the above code is the HTTP module’s createServer method, which generates an HTTP server instance. This method takes a callback that takes a Request object representing an HTTP request and a Response object representing an HTTP response.

An object placed into the Router array when a loop request comes in. When the request method and path match those in the object, the callback handler method is executed.

Multiple routes multiple callbacks and middleware

  1. The test case
const express = require('.. /lib/express'); const app = express(); /** * 1. Get specifies multiple handlers * 2. Middleware error handling * 3. */ ** * app.use * express.router (); */ app.use(function (req, res, next) {
    console.log('Ware1:', Date.now()); next(); }); // The route is fully matched. /! App.get () = /user'/'.function (req, res, next) {
    res.end('1'); }); // Create a new routing container, or routing system const user = express.router (); // router user.use(function (req, res, next) {
    console.log('Ware2', Date.now()); next(); }); // The path in the child path is relative to the parent path user.get('/ 2'.function (req, res, next) {
    res.end('2'); }); //use indicates the use of middleware, just need to match the prefix app.use('/user', user); // req. Url = /user/3 //app.use()'/user', artcile);
app.use(function (err, req, res, next) {
    res.end('catch ' + err);
});
app.listen(3000, function () {
    console.log('server started at port 3000');
});
Copy the code
  1. First, reform the project structure
iExpress/
|
|   
| - application.js  Wrap the app layer
|
| - route/
|   | - index.js    # the Router class
|   | - route.js    # the Route class
|   | - layer.js    # Layer class
|
| - middle/
|   | - init.js     # Built-in middleware
|
| - test/ | | - test case file 1 | | -... | · - express. Js# frame entry
Copy the code
  • App goes from literal to Application class
  • Enrich HTTP request methods
  • To encapsulate the Router
  • Routes with the same path are integrated into a group and the concept of Layer is introduced
  • Added routing control, support for the next method, and error catching
  • Router.handle is passed as an out parameter
  1. Clear logic

The test code registers multiple routes and can add multiple callback methods, dividing the logic into three steps.

(1) The Application container distributes the request method and handler to the router. When executing the listen function, execute self._router.handle(req, res, done) to make the logic inserted into the router run.

The Application class

const Router = require('./router');

Application.prototype.lazyrouter = function () {
    if(! this._router) { this._router = new Router(); } } methods.forEach(function (method) {
    Application.prototype[method] = function() { this.lazyrouter(); This._router [method].apply(this._router, slice.call(arguments));returnthis; }}); Application.prototype.listen =function () {
    let self = this;
    let server = http.createServer(function (req, res) {
        function doneRes.end (' Cannot ') {// The function res.end(' Cannot 'is used if no routing rule matches${req.method} ${req.url}`); } // If the routing system cannot handle it, i.e. no routing rule matches the request, the request will be handed overdone
        self._router.handle(req, res, done); }); server.listen(... arguments); }Copy the code

(2) Each method request in the Router will add a layer to the current routing system, and create a Route instance in the layer

The Router class

proto.route = function (path) {
    let route = new Route(path);
    letlayer = new Layer(path, route.dispatch.bind(route)); layer.route = route; this.stack.push(layer); // Add a layer to the Routerreturn route;
}

methods.forEach(function (method) {
    proto[method] = function(path) {// request to comeletroute = this.route(path); // Add route[method]. Apply (route, slice.call(arguments, 1)); //returnthis; }});Copy the code

If it is middleware, there is no path by default so the route of layer is set to undefined

proto.use = function (path, handler) {
    if(typeof handler ! ='function') {
        handler = path;
        path = '/';
    }
    letlayer = new Layer(path, handler); layer.route = undefined; This.stack.push (layer); this.stack.push(layer);return this
}
Copy the code

When Application starts listening on the port, it executes the Handle method on the Router. Adding the next function is responsible for handing control to the next middleware. If the current middleware does not terminate the request and next is not called, the request will be suspended and the middleware defined later will not have a chance to execute.

When the path in the Router matches the method, go to the current layer and run layer.handle_request to execute the method added in route.

proto.handle = function(req, res, out) {//slashAdded added/removed refers to a string that was removedlet idx = 0,
        self = this,
        slashAdded = false,
        removed = ' ';
    // /user/2
    let { pathname } = url.parse(req.url, true);
    function next(err) {
        if (slashAdded) {
            req.url = ' ';
            slashAdded = false;
        }
        if (removed.length > 0) {
            req.url = removed + req.url;
            removed = ' ';
        }
        if (idx >= self.stack.length) {
            return out(err);
        }
        letlayer = self.stack[idx++]; // Match path params re +url= req.params hereif (layer.match(pathname)) {// layer.params
            if(! Layer. Route) {// This layer is removed // user/2 removed = layer.path; // /user req.url = req.url.slice(removed.length); / / / 2if (err) {
                    layer.handle_error(err, req, res, next);
                } else {
                    if (req.url == ' ') {
                        req.url = '/';
                        slashAdded = true; } layer.handle_request(req, res, next); }}else {
                if(layer.route && layer.route.handle_method(req.method)) {// copy the parmas attribute of the layer to req.params req.params = layer.params; self.process_params(layer, req, res, () => { layer.handle_request(req, res, next); }); }else{ next(err); }}}else {
            next(err);
        }
    }
    next();
}
Copy the code

(3) Enter the current layer and execute each route added in sequence

Layer class

Layer.prototype.handle_request = function (req, res, next) {
    this.handler(req, res, next);
}
Copy the code

Route.dispatch.bind (route) is a route.dispatch.bind(route) method that is used to initialize the layer. Parse the Dispatch code:

Route.prototype.dispatch = function (req, res, out) {
    let idx = 0, self = this;
    function next(err) {
        if(err) {// If an error occurs in the routing function, the current route will be skippedreturn out(err);
        }
        if (idx >= self.stack.length) {
            returnout(); // Dispath out is Router next}let layer = self.stack[idx++];
        if (layer.method == req.method.toLowerCase()) {
            layer.handle_request(req, res, next);
        } else {
            next();
        }
    }
    next();
}
Copy the code

The text structure is shown below

Application
|
|
Router
|
| - stack
    |
    | - Layer
        |
        | - path  router
                    |
                    | - method  handler
            
Copy the code

Router Layer

  • The Router Layer path handler (route.dispatch) has a special Route attribute
  • The Route Layer path handler function (real business code) has a special property method

Application only performs packaging magic and route distribution. Router implements encapsulation of routing methods such as app.use, app.param, app.get, and app.post

Logic diagram

The source code

Warehouse Address:Source link point here ~