一、 Application 模組的簡單封裝
首先我們先簡單封裝一個模組 Application 保證服務的正常執行;
- 初始化一個專案
$ npm init -y
...
複製程式碼
- 建立檔案 application.js 並並編寫如下程式碼;
const http = require('http');
class Application{
// 初始化
constructor(){
this.callback = () => {}
}
// 設定回撥函式
use(callback){
this.callback = callback;
}
// listen 建立服務並對服務進行監聽
listen(...args){
const server = http.createServer((req, res) => {
this.callback(req, res);
});
server.listen(...args);
}
}
module.exports = Application;
複製程式碼
- 建立 server.js 檔案,呼叫 Application 模組起一個服務:
const Application = require('./application.js');
const app = new Application();
app.use((req, res) => {
res.writeHead(200);
res.end('hello world');
});
app.listen(3000, () => {
console.log('監聽埠:3000');
});
複製程式碼
二、 Application 模組掛載 context
首先我們假設我們的 context 是這麼一個資料結構:
- context 中掛載了 request response req res, 同時還有抽離的額外屬性 url body
- request 中掛載了 req, 同時還有抽離的額外屬性 url
- response 中掛載了 res, 同時還有抽離的額外屬性 body
context: {
url: String,
body: String,
request: {
url: String,
req: Object
},
response: {
body: String,
res: Object
},
req: Object,
res: Object
}
複製程式碼
改寫 Application
- 設計 context request response 原型資料結構;
- 將 context request response 原型資料結構掛載到 Application
- 編寫函式建立 context
- 改寫回撥函式的呼叫方式;
const http = require('http');
// [1]構建資料結構(作為原型使用)
const request = {
// 因為後期 request 將會掛載上 req 所以存在 this.req
get url(){
return this.req.url;
}
};
const response = {
get body(){
return this._body;
},
set body(val){
this._body = val;
}
};
const context = {
// 因為後期 context 將會掛載上 request response 所以存在 this.request 和 this.response
get url(){
return this.request.url;
},
get body(){
return this.response.body;
},
set body(val){
this.response.body = val;
}
}
class Application{
constructor(){
this.callback = () => {},
// [2]將原型掛載到 Application
this.context = context;
this.request = request;
this.response = response;
}
use(callback){
this.callback = callback;
}
// [3]建立 context 函式,掛載上 request response req res
createCtx(req, res){
const ctx = Object.create(this.context);
ctx.request = Object.create(this.request);
ctx.response = Object.create(this.response);
ctx.req = ctx.request = req;
ctx.res = ctx.response = res;
return ctx;
}
listen(...args){
const server = http.createServer((req, res) => {
// [4]建立 context, 並進行簡單修改
const ctx = this.createCtx(req, res);
this.callback(ctx);
ctx.res.end(ctx.body);
});
server.listen(...args);
}
}
module.exports = Application;
複製程式碼
修改 server.js 中 Application 的引用
const Application = require('./application.js');
const app = new Application();
app.use( ctx => {
ctx.body = 'hello world'
});
app.listen(3000, () => {
console.log('監聽埠:3000');
});
複製程式碼
三、 中介軟體的實現
3.1 洋蔥模型實現
// 場景模擬
// 非同步 promise 模擬
const delay = async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
// 中間間模擬
const fn1 = async (ctx, next) => {
console.log(1);
await next();
console.log(2);
}
const fn2 = async (ctx, next) => {
console.log(3);
await delay();
await next();
console.log(4);
}
const fn3 = async (ctx, next) => {
console.log(5);
}
const middlewares = [fn1, fn2, fn3];
// compose 實現洋蔥模型
const compose = (middlewares, ctx) => {
const dispatch = (i) => {
let fn = middlewares[i];
if(!fn){ return Promise.resolve() }
return Promise.resolve(fn(ctx, () => {
return dispatch(i+1);
}));
}
return dispatch(0);
}
compose(middlewares, 1);
複製程式碼
3.2 compose 函式在 Application 模組中的使用:
const http = require('http');
const request = {
get url(){
return this.req.url;
}
};
const response = {
get body(){
return this._body;
},
set body(val){
this._body = val;
}
};
const context = {
get url(){
return this.request.url;
},
get body(){
return this.response.body;
},
set body(val){
this.response.body = val;
}
}
class Application{
constructor(){
this.context = context;
this.request = request;
this.response = response;
// 初始化中介軟體陣列
this.middlewares = [];
}
// 通過push的方式進行新增中介軟體
use(middleware){
this.middlewares.push(middleware);
}
createCtx(req, res){
const ctx = Object.create(this.context);
ctx.request = Object.create(this.request);
ctx.response = Object.create(this.response);
ctx.req = ctx.request = req;
ctx.res = ctx.response = res;
return ctx;
}
// compose 函式
compose(middlewares, ctx){
const dispatch = (i) => {
const fn = middlewares[i];
if(!fn){
return Promise.resolve();
}else{
return Promise.resolve(fn(ctx, () => {
dispatch(i +1 );
}));
}
}
return dispatch(0);
}
listen(...args){
// 改用 async await 並呼叫compose
const server = http.createServer(async (req, res) => {
const ctx = this.createCtx(req, res);
await this.compose(this.middlewares, ctx);
ctx.res.end(ctx.body);
});
server.listen(...args);
}
}
module.exports = Application;
複製程式碼