express 基本功能
首先看一个简单的实例,通过这个实例可以看出express大体需要做哪些事情。
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
从这个实例中大体可以看出express提供一个入口方法express,返回一个实例,实例上提供一个监听方法,监听对应端口的http请求,以及提供一些类似get这种,路由监听方法
基础结构实现
function createApplication(): any {
// app是一个监听函数
let app = function (req: Request, res) {
};
// @ts-ignore
app.listen = function () {
let server = http.createServer(app);
// arguments就是参数(3000, 'localhost', function () {})
server.listen(...arguments);
return server;
};
return app;
}
const app = createApplication();
app.listen(3000, () => {
console.log("listen 3000");
});
主要使用nodeJs原生的createServer方法,创建server,通过listen方法暴露出来。
路由监听方法
框架要提供所有http请求方法的对应路由监听方法,可以通过http模块的METHODS获取,获取到后遍历然后将对应的路由监听回调方法存储在路由数组中。
app.routes = [];
http.METHODS.forEach((method) => {
method = method.toLocaleLowerCase();
app[method] = function (path: string, handler) {
app.routes.push({
path,
method,
handler,
});
};
});
路由监听回调函数执行
最后可以将执行路由回调函数的任务放在app方法中做,该方法在每次有http请求的时候都会被执行。
let app: App = function (req, res) {
let method = req.method.toLocaleLowerCase();
let { pathname } = url.parse(req.url, true);
app.routes.forEach((route) => {
if (route.method === method && route.path === pathname) {
route.handler(req, res);
}
});
};
目前大体的基本功能已经实现,完整代码如下:
import * as http from "http";
import * as url from "url";
import { App } from "./config";
function createApplication(): App {
// @ts-ignore
let app: App = function (req, res) {
let method = req.method.toLocaleLowerCase();
let { pathname } = url.parse(req.url, true);
app.routes.forEach((route) => {
if (route.method === method && route.path === pathname) {
route.handler(req, res);
}
});
};
app.routes = [];
app.listen = function () {
// @ts-ignore
let server = http.createServer(app);
// arguments就是参数(3000, 'localhost', function () {})
server.listen(...arguments);
return server;
};
http.METHODS.forEach((method) => {
method = method.toLocaleLowerCase();
app[method] = function (path: string, handler) {
app.routes.push({
path,
method,
handler,
});
};
});
return app;
}
const app = createApplication();
app.listen(3000, (req,res) => {
console.log("listen 3000");
});
app.get("/gettest", (req, res) => {
res.end('hello get test')
});
中间件功能
实现中间件功能其实很简单,中间件可以看做一个特殊的路由监听事件。 中间件通过use方法注册:
app.use = function (path, handler) {
app.routes.push({
path,
method: MIDDLE_WARE_METHOD,
handler,
});
};
接着在app方法中加入中间件判断
const MIDDLE_WARE_METHOD = "middleware";
let app: App = function (req, res) {
let httpMethod = req.method.toLocaleLowerCase();
let { pathname } = url.parse(req.url, true);
let index = 0;
function next() {
if (index === app.routes.length) {
return;
}
let { method, path, handler } = app.routes[index++];
if (method === MIDDLE_WARE_METHOD) {
if (path === pathname) {
handler(req, res, next);
} else {
next();
}
} else {
if (method === httpMethod && pathname === path) {
handler(req, res);
} else {
next();
}
}
}
next();
};
在app方法中加入next方法以及是否是中间件路由监听的判断
完整代码
import * as http from "http";
import * as url from "url";
import { IncomingMessage, ServerResponse } from "http";
import { App } from "./interface";
const MIDDLE_WARE_METHOD = "middleware";
function createApplication(): App {
let app = <App>function (req: IncomingMessage, res: ServerResponse) {
let httpMethod = req.method.toLocaleLowerCase();
let { pathname } = url.parse(req.url, true);
let index = 0;
function next() {
if (index === app.routes.length) {
return;
}
let { method, path, handler } = app.routes[index++];
if (method === MIDDLE_WARE_METHOD) {
if (path === pathname) {
handler(req, res, next);
} else {
next();
}
} else {
if (method === httpMethod && pathname === path) {
handler(req, res);
} else {
next();
}
}
}
next();
};
app.use = function (path, handler) {
app.routes.push({
path,
method: MIDDLE_WARE_METHOD,
handler,
});
};
app.routes = [];
app.listen = function (...arg) {
let server = http.createServer(app);
// arguments就是参数(3000, 'localhost', function () {})
server.listen(...arg);
return server;
};
http.METHODS.forEach((method) => {
method = method.toLocaleLowerCase();
app[method] = function (path: string, handler) {
app.routes.push({
path,
method,
handler,
});
};
});
return app;
}
const app = createApplication();
app.listen(3000, () => {
console.log("listen 3000");
});
app.get("/gettest", (req, res) => {
res.end("hello get test");
});
app.use("/test", (req, res, next) => {
next();
});
总结
这个只是简单的mock,还有很多功能没有完善,大家可以有选择的进行阅读,后续我会逐步完善。