上一節(jié)實現(xiàn)了 express 的中間件,這一節(jié)來實現(xiàn)錯誤處理中間件
執(zhí)行某一步出錯了,統(tǒng)一規(guī)定調(diào)用 next 傳遞的參數(shù)就是錯誤信息
先看 express 實現(xiàn)的demo
const express = require("express");
const app = express();
app.use("/", (req, res, next) => {
console.log("中間件1");
// next();
next("中間件1出錯了");
});
app.use("/", (req, res, next) => {
console.log("中間件2");
next();
// next("中間件2出錯了");
});
app.use("/", (req, res, next) => {
console.log("中間件3");
next();
// next("中間件3出錯了");
});
app.get(
"/",
(req, res, next) => {
console.log("路由1");
next();
},
(req, res, next) => {
res.end("出錯了 *****");
}
);
app.listen(3000, () => {
console.log(`server start 3000`);
console.log(`在線訪問地址:http://localhost:3000/`);
});
然后去訪問:http://localhost:3000/
錯誤處理中間價,里面必須要有 4 個 參數(shù)(取函數(shù)的長度),放到棧的最底下
app.use((err, req, res, next) => {
res.end(err);
})
下面實現(xiàn)處理邏輯
router/index.js
const url = require("url");
const Route = require("./route");
const Layer = require("./layer");
const methods = require("methods");
function Router() {
// 維護(hù)所有的路由
this.stack = [];
}
Router.prototype.route = function (path) {
// 產(chǎn)生 route
let route = new Route();
// 產(chǎn)生 layer 讓 layer 跟 route 進(jìn)行關(guān)聯(lián)
let layer = new Layer(path, route.dispatch.bind(route));
// 每個路由都具備一個 route 屬性,稍后路徑匹配到后會調(diào)用 route 中的每一層
layer.route = route;
// 把 layer 放到路由的棧中
this.stack.push(layer);
return route;
};
methods.forEach((method) => {
Router.prototype[method] = function (path, handlers) {
// 1.用戶調(diào)用 method 時,需要保存成一個 layer 當(dāng)?shù)罈V?/span>
// 2.產(chǎn)生一個 Route 實例和當(dāng)前的 layer 創(chuàng)造關(guān)系
// 3.要將 route 的 dispatch 方法存到 layer 上
let route = this.route(path);
// 讓 route 記錄用戶傳入的 handler 并且標(biāo)記這個 handler 是什么方法
route[method](handlers);
};
});
Router.prototype.use = function (path, ...handlers) {
// 默認(rèn)第一個是路徑,后面是一個個的方法,路徑可以不傳
if (typeof path === "function") {
handlers.unshift(path);
path = "/";
}
// 如果是多個函數(shù)需要循環(huán)添加層
for (let i = 0; i < handlers.length; i++) {
let layer = new Layer(path, handlers[i]);
// 中間件不需要 route 屬性
layer.route = undefined;
this.stack.push(layer);
}
};
Router.prototype.handle = function (req, res, out) {
console.log("請求到了");
// 需要取出路由系統(tǒng)中 Router 存放的 layer 依次執(zhí)行
const { pathname } = url.parse(req.url);
let idx = 0;
let next = (err) => {
// 遍歷完后沒有找到就直接走出路由系統(tǒng)
if (idx >= this.stack.length) return out();
let layer = this.stack[idx++];
if (err) {
console.log("統(tǒng)一對中間件跟路由錯誤處理");
// 找錯誤處理中間件
if (!layer.route) {
// 如果是中間件自己處理
layer.handle_error(err, req, res, next);
} else {
// 路由則跳過,繼續(xù)攜帶錯誤向下執(zhí)行
next(err);
}
} else {
// 需要判斷 layer 上的 path 和當(dāng)前請求路由是否一致,一致就執(zhí)行 dispatch 方法
if (layer.match(pathname)) {
// 中間件沒有方法可以匹配,不能是錯誤處理中間件
if (!layer.route) {
if (layer.handler.length !== 4) {
layer.handle_request(req, res, next);
} else {
next();
}
} else {
// 將遍歷路由系統(tǒng)中下一層的方法傳入
// 加速匹配,如果用戶注冊過這個類型的方法在去執(zhí)行
if (layer.route.methods[req.method.toLowerCase()]) {
layer.handle_request(req, res, next);
} else {
next();
}
}
} else {
next();
}
}
};
next();
};
module.exports = Router;
layer.js
function Layer(path, handler) {
this.path = path;
this.handler = handler;
}
Layer.prototype.match = function (pathname) {
if (this.path === pathname) {
return true;
}
// 如果是中間件,進(jìn)行中間件的匹配規(guī)則
if (!this.route) {
if (this.path == "/") {
return true;
}
// /aaaa/b 需要 /aaaa/ 才能匹配上
return pathname.startsWith(this.path + "/");
}
return false;
};
Layer.prototype.handle_error = function (err, req, res, next) {
if (this.handler.length === 4) {
// 調(diào)用錯誤處理中間件
return this.handler(err, req, res, next);
}
next(err); // 普通的中間件
};
Layer.prototype.handle_request = function (req, res, next) {
this.handler(req, res, next);
};
module.exports = Layer;
route.js
const Layer = require("./layer");
const methods = require("methods");
function Route() {
this.stack = [];
// 用來描述內(nèi)部存過哪些方法
this.methods = {};
}
Route.prototype.dispatch = function (req, res, out) {
// 稍后調(diào)用此方法時,回去棧中拿出對應(yīng)的 handler 依次執(zhí)行
let idx = 0;
console.log("this.stack----->", this.stack);
let next = (err) => {
// 如果內(nèi)部迭代的時候出現(xiàn)錯誤,直接到外層的 stack 中
err && out(err);
// 遍歷完后沒有找到就直接走出路由系統(tǒng)
if (idx >= this.stack.length) return out();
let layer = this.stack[idx++];
console.log("dispatch----->", layer.method);
if (layer.method === req.method.toLowerCase()) {
layer.handle_request(req, res, next);
} else {
next();
}
};
next();
};
methods.forEach((method) => {
Route.prototype[method] = function (handlers) {
console.log("handlers----->", handlers);
handlers.forEach((handler) => {
// 這里的路徑?jīng)]有意義
let layer = new Layer("/", handler);
layer.method = method;
// 做個映射表
this.methods[method] = true;
this.stack.push(layer);
});
};
});
module.exports = Route;
測試demo文章來源:http://www.zghlxwxcb.cn/news/detail-730397.html
const express = require("./kaimo-express");
const app = express();
app.use("/", (req, res, next) => {
console.log("中間件1");
next();
// next("中間件1出錯了");
});
app.use("/", (req, res, next) => {
console.log("中間件2");
// next();
next("中間件2出錯了");
});
app.use("/", (req, res, next) => {
console.log("中間件3");
next();
// next("中間件3出錯了");
});
app.get(
"/",
(req, res, next) => {
console.log("路由1");
next();
},
(req, res, next) => {
res.end("出錯了 *****");
}
);
// 錯誤處理中間價
app.use((err, req, res, next) => {
console.log("錯誤處理中間價----->", err);
res.end(err);
});
app.listen(3000, () => {
console.log(`server start 3000`);
console.log(`在線訪問地址:http://localhost:3000/`);
});
文章來源地址http://www.zghlxwxcb.cn/news/detail-730397.html
到了這里,關(guān)于93 # 實現(xiàn) express 錯誤處理中間件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!