express

1. Realization of basic functions

1.1. Test 1:

const express=require('./express');
const app=express();

app.get('/'.(req,res,next) = >{
    console.log(1);
    next();
    console.log(2)},(req,res,next) = >{
    console.log(3);
    next();
    console.log(4)},(req,res,next) = >{
    console.log(5);
    next();
    console.log(6)
})

app.get('/'.(req,res,next) = >{
    res.end('ok');
})
app.listen(3000)
Copy the code

Log:

http://localhost:3000/

1
3
5
6
4
2
Copy the code

1.2. Test 2:

const express = require('./express');
const app = express();

app.get('/'.(req, res, next) = > {
    console.log('get');
    res.end('get')
})
app.post('/'.(req, res, next) = > {
    console.log('post');
    res.end('post')
})
app.listen(3000)
Copy the code

Log:

curl -v -X GET http://localhost:3000

get
Copy the code

curl -v -X POST http://localhost:3000

post
Copy the code

1.3. To achieve

. ├ ─ ─ the README. Md ├ ─ ─ express │ ├ ─ ─ application. Js │ ├ ─ ─ express. Js │ ├ ─ ─ package. The json │ └ ─ ─ the router │ ├ ─ ─ index. The js │ ├ ─ ─ Layer. The js │ └ ─ ─ the route. JsCopy the code
1.3.1.express.js
const Application = require('./application');
function createApplication() {
    return new Application();
}
module.exports = createApplication;
Copy the code
1.3.2.application.js
const http = require('http');
const Router = require('./router');
const methods = require('methods');
function Application() {
}

Application.prototype.lazy_router=function(){
    if(!this._router) this._router=new Router;
}

methods.forEach(method= > {
    Application.prototype[method] = function (path, ... handlers) {
        this.lazy_router();
        this._router[method](path, handlers);
    }
})


Application.prototype.listen = function (. args) {
    const server = http.createServer((req, res) = > {
        this.lazy_router();
        // called when Route cannot be processed
        const done = () = > {
            res.end(`Cannot ${req.method} ${req.url}`)}this._router.handler(req, res, done); }) server.listen(... args); }module.exports = Application;
Copy the code
1.3.3.router/index.js
const Layer = require('./layer');
const Route = require('./route');
const url = require('url');
const methods = require('methods');
function Router() {
    this.stack = [];/ / deposit Layer
}

Router.prototype.route = function (path) {
    const route = new Route;
    const layer = new Layer(path, route.dispatch.bind(route));
    layer.route = route;// Layer and route management
    this.stack.push(layer);
    return route;
}

methods.forEach(method= > {
    Router.prototype[method] = function (path, handlers) {
        const route = this.route(path);
        route[method](handlers)
    }
})


Router.prototype.handler = function (req, res, done) {
    const { pathname } = url.parse(req.url);
    const method=req.method.toLowerCase();
    let idx = 0;
    const next = () = > {
        if (idx >= this.stack.length) return done();/ / launch
        const layer = this.stack[idx++]
        if (layer.match(pathname) && layer.route.methods[method]) {
            layer.handler(req, res, next);
        } else {
            next();
        }
    }
    next();
}

module.exports = Router;
Copy the code
1.3.4.router/layer.js
function Layer(path, handler) {
    this.path = path;
    this.handler = handler;
}

Layer.prototype.match=function(pathname){
    return this.path === pathname
}

module.exports = Layer;
Copy the code
1.3.5.router/route.js
const Layer = require('./layer');
const methods = require('methods');
function Route() {
    this.stack = [];
    this.methods = {};
}

methods.forEach(method= > {
    Route.prototype[method] = function (handles) {
        for (let i = 0; i < handles.length; i++) {
            // The memory layer stores events and methods
            const layer = new Layer(' ', handles[i]);
            layer.method = method;
            this.methods[method] = true;
            this.stack.push(layer);
        }
    }
})


Route.prototype.dispatch = function (req, res, done) {
    const method = req.method.toLowerCase();
    let idx = 0;
    const next = () = > {
        if (idx >= this.stack.length) return done();
        const layer = this.stack[idx++];
        if (layer.method === method) {
            layer.handler(req, res, next);
        } else {
            next();
        }
    }
    next();
}

module.exports = Route;
Copy the code

2. Middleware use

2.1. Test

const express = require('./express');
const app = express();

app.use(function(req,res,next){
    console.log('All requests go through me.');
    next();
});

app.use('/user'.function(req,res,next){
    console.log('User request goes through me');
    next()
});

app.use('/cart'.function(req,res,next){
    console.log('Cart request goes through me');
    next()
});

app.use('/user/add'.function(req,res,next){
    console.log('user/add');
    res.end('/user/add')}); app.use('/user/cart'.function(req,res,next){
    console.log('user/cart');
    res.end('/user/cart')}); app.listen(3000)
Copy the code

Log:

http://localhost:3000/user/add

All requests go through me user and requests that start with user go through me user/addCopy the code

2.2. To achieve

2.2.1.application.js
Application.prototype.use=function(path,... handlers){
    this.lazy_router();
    this._router.use(path,handlers);
}
Copy the code
2.2.2.router/index.js
Router.prototype.handler = function (req, res, done) {
    const { pathname } = url.parse(req.url);
    const method = req.method.toLowerCase();
    let idx = 0;
    const next = () = > {
        if (idx >= this.stack.length) return done();/ / launch
        const layer = this.stack[idx++]
+       if (layer.match(pathname)) {// Path matching, either routing or middleware
+           if(! layer.route) {/ / middleware+ layer.handler(req,res,next); +}else {/ / routing
+               if (layer.route.methods[method]) {// Route matches, method matches, otherwise execute more+ layer.handler(req, res, next); +}else{ + next(); +} +}}else {
            next();
        }
    }
    next();
}

+ Router.prototype.use = function (path, handlers) {+if(! handlers[0]) {
+       handlers = [path];
+       path = '/'+},for (let i = 0; i < handlers.length; i++) {
+       const layer = new Layer(path, handlers[i]);
+       layer.route = undefined;// It is used to mark middleware
+       this.stack.push(layer); + +}}Copy the code
2.2.3.router/layer.js
Layer.prototype.match = function (pathname) {
    if (pathname === this.path) return true;
    // Middleware starts with /, or middleware starts with path+'/'
    if (!this.route && (this.path === '/' || pathname.startsWith(this.path + '/'))) return true;
    return false
}
Copy the code

3. Error handling

3.1. Test

const express = require('./express');
const app = express();

app.use(function (req, res, next) {
    console.log('All requests go through me.');
    next();
});

app.use('/user'.function (req, res, next) {
    console.log('User request goes through me');
    next()
});

app.use('/cart'.function (req, res, next) {
    console.log('Cart request goes through me');
    next()
});

app.use('/user/add'.function (req, res, next) {
    next('User /add error, hahaha')}); app.use('/user/cart'.function (req, res, next) {
    next('User /cart error, hee hee hee')}); app.use((err, req, res, next) = > {
    res.setHeader('Content-Type'.'text/plain; charset=utf8')
    res.end(err);
})

app.listen(3000)
Copy the code

Log:

http://localhost:3000/user/add

User /add error, hahaha log: all requests will go through meCopy the code

3.2. To achieve

3.2.1.router/route.js
Route.prototype.dispatch = function (req, res, done) {
    const method = req.method.toLowerCase();
    let idx = 0;
+    const next = (err) = >{+if(err) return done(err);// Memory detects an error and pushes the route layer directly with the error
        if (idx >= this.stack.length) return done();
        const layer = this.stack[idx++];
        if (layer.method === method) {
            layer.handler(req, res, next);
        } else {
            next();
        }
    }
    next();
}
Copy the code
3.2.2.router/index.js
Router.prototype.handler = function (req, res, done) {
    const { pathname } = url.parse(req.url);
    const method = req.method.toLowerCase();
    let idx = 0;
    const next = (err) = > {
        if (idx >= this.stack.length) return done();/ / launch
        const layer = this.stack[idx++]
        if (err) {
            // If there is an error, find the middleware to handle the error
            if(! layer.route && layer.handler.length ===4) {// is middleware, and the parameter is 4, which represents the middleware that caught the error
                layer.handler(err, req, res, next);
            } else {
                next(err);// Continue to carry the error one level}}else {
            if (layer.match(pathname)) {// Path matching, either routing or middleware
                if(! layer.route) {/ / middleware
                    if(layer.handler.length ! = =4) {// Normal middleware
                        layer.handler(req, res, next);
                    } else {// Error middlewarenext(); }}else {/ / routing
                    if (layer.route.methods[method]) {// Route matches, method matches, otherwise execute more
                        layer.handler(req, res, next);
                    } else{ next(); }}}else {
                next();
            }
        }

    }
    next();
}
Copy the code

4. Route with parameters

4.1. Test

const express = require('./express');
const app = express();

app.use(function(req,res,next){
    res.send=function(data){
        if(typeof data==='object'&&data! = =null){
            res.setHeader('Content-Type'.'application/json');
            res.end(JSON.stringify(data));
        }
    }
    next();
})
app.get('/user/:id/:name/detail'.function (req, res, next) {
    const params=req.params;
    console.log(params)
    res.send(params);
});


app.listen(3000)
Copy the code

Log:

http://localhost:3000/user/123/zhangsan/detail

{ id: '123'.name: 'zhangsan' }
Copy the code

4.2. To achieve

2.router/layer.js
+ const pathToRegexp = require('path-to-regexp');

function Layer(path, handler) {
    this.path = path;
    this.handler = handler;
+   this.regExp = pathToRegexp(path, this.keys = []);
}

Layer.prototype.match = function (pathname) {
    if (pathname === this.path) return true;

    // Route with parameters
+   const r = pathname.match(this.regExp);
+   if (r) {
+       const [, ...matchs] = r;// The result of the re match
+       this.params = {};
+       this.keys.forEach((key,index) = >{+this.params[key.name] = matchs[index]; + +})return true; +}// Middleware starts with /, or middleware starts with path+'/'
    if (!this.route && (this.path === '/' || pathname.startsWith(this.path + '/'))) return true;
    return false
}
Copy the code
4.2.2.router/index.js
Router.prototype.handler = function (req, res, done) {
    const { pathname } = url.parse(req.url);
    const method = req.method.toLowerCase();
    let idx = 0;
    const next = (err) = > {
        if (idx >= this.stack.length) return done();/ / launch
        const layer = this.stack[idx++]
        if (err) {
            // If there is an error, find the middleware to handle the error
            if(! layer.route && layer.handler.length ===4) {// is middleware, and the parameter is 4, which represents the middleware that caught the error
                layer.handler(err, req, res, next);
            } else {
                next(err);// Continue to carry the error one level}}else {
            if (layer.match(pathname)) {// Path matching, either routing or middleware
                // Parameter Settings
+               req.params = layer.params;
                if(! layer.route) {/ / middleware
                    if(layer.handler.length ! = =4) {// Normal middleware
                        layer.handler(req, res, next);
                    } else {// Error middlewarenext(); }}else {/ / routing
                    if (layer.route.methods[method]) {// Route matches, method matches, otherwise execute more
                        layer.handler(req, res, next);
                    } else{ next(); }}}else {
                next();
            }
        }

    }
    next();
}
Copy the code

5. Secondary route

Example 5.1.

  • server.js
const express = require('express');
const user = require('./routes/user');
const home = require('./routes/home');
const app = express();

app.use('/home', home);
app.use('/user', user);

app.listen(3000)
Copy the code
  • routes/home.js
const express=require('express');
const router=express.Router()

router.get('/add'.function(req,res){
    res.end('/home/add')
})

router.get('/remove'.function(req,res){
    res.end('/home/remove')})module.exports=router;
Copy the code
  • routes/user.js
const express=require('express');
const router=express.Router()

router.get('/add'.function(req,res){
    res.end('/user/add')
})

router.get('/remove'.function(req,res){
    res.end('/user/remove')})module.exports=router;
Copy the code

5.2. To achieve

5.2.1.express.js
  const Application = require('./application');
+ const Router = require('./router');
  function createApplication() {
     return new Application();
  }
+ createApplication.Router = Router;
  module.exports = createApplication;
Copy the code
5.2.2.router/index.js
const Layer = require('./layer');
const Route = require('./route');
const url = require('url');
const methods = require('methods');
function Router() {+const router = function (req, res, next) {
        // When the request arrives, this method is executed after the path is matched, and it needs to be executed in the stack one by one
+       router.handler(req, res, next);
+   }
+   router.stack = [];
+   router.__proto__ = proto;
+   return router;
}
  
+ const proto = {};
+ proto.route = function (path) {
    const route = new Route;
    const layer = new Layer(path, route.dispatch.bind(route));
    layer.route = route;// Layer and route management
    this.stack.push(layer);
    return route;
}

methods.forEach(method= > {
+   proto[method] = function (path, handlers) {+if(!Array.isArray(handlers)){
+           handlers=Array.from(arguments).slice(1); +}const route = this.route(path);
        route[method](handlers)
    }
})


+ proto.handler = function (req, res, done) {
    const { pathname } = url.parse(req.url);
    const method = req.method.toLowerCase();
    let idx = 0;
+   let removed = ' ';// To record the deleted path
    const next = (err) = > {
        if (idx >= this.stack.length) return done();/ / launch
        const layer = this.stack[idx++];

+       if (removed.length) {// From the inside
+           req.url = removed + req.url;
+           removed = ' '; +}if (err) {
            // If there is an error, find the middleware to handle the error
            if(! layer.route && layer.handler.length ===4) {// is middleware, and the parameter is 4, which represents the middleware that caught the error
                layer.handler(err, req, res, next);
            } else {
                next(err);// Continue to carry the error one level}}else {
            if (layer.match(pathname)) {// Path matching, either routing or middleware
                // Parameter Settings
                req.params = layer.params;
                if(! layer.route) {/ / middleware
                    if(layer.handler.length ! = =4) {// Normal middleware
                        // The path of the middleware needs to be removed when entering the middleware
+                       if(layer.path ! = ='/') {
+                           removed = layer.path;
+                           req.url = req.url.slice(layer.path.length);
+                       }
                        
                        layer.handler(req, res, next);
                    } else {// Error middlewarenext(); }}else {/ / routing
                    if (layer.route.methods[method]) {// Route matches, method matches, otherwise execute more
                        layer.handler(req, res, next);
                    } else{ next(); }}}else {
                next();
            }
        }

    }
    next();
}

proto.use = function (path, handlers) {
    if(! handlers[0]) {
        handlers = [path];
        path = '/'
    }
    for (let i = 0; i < handlers.length; i++) {
        const layer = new Layer(path, handlers[i]);
        layer.route = undefined;// It is used to mark middleware
        this.stack.push(layer); }}module.exports = Router;
Copy the code