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