93 # 实现 express 错误处理中间件

news2024/11/23 23:40:57

上一节实现了 express 的中间件,这一节来实现错误处理中间件

执行某一步出错了,统一规定调用 next 传递的参数就是错误信息

先看 express 实现的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 个 参数(取函数的长度),放到栈的最底下

app.use((err, req, res, next) => {
    res.end(err);
})

在这里插入图片描述

下面实现处理逻辑

router/index.js

const url = require("url");
const Route = require("./route");
const Layer = require("./layer");
const methods = require("methods");

function Router() {
    // 维护所有的路由
    this.stack = [];
}

Router.prototype.route = function (path) {
    // 产生 route
    let route = new Route();
    // 产生 layer 让 layer 跟 route 进行关联
    let layer = new Layer(path, route.dispatch.bind(route));
    // 每个路由都具备一个 route 属性,稍后路径匹配到后会调用 route 中的每一层
    layer.route = route;
    // 把 layer 放到路由的栈中
    this.stack.push(layer);
    return route;
};

methods.forEach((method) => {
    Router.prototype[method] = function (path, handlers) {
        // 1.用户调用 method 时,需要保存成一个 layer 当道栈中
        // 2.产生一个 Route 实例和当前的 layer 创造关系
        // 3.要将 route 的 dispatch 方法存到 layer 上
        let route = this.route(path);
        // 让 route 记录用户传入的 handler 并且标记这个 handler 是什么方法
        route[method](handlers);
    };
});

Router.prototype.use = function (path, ...handlers) {
    // 默认第一个是路径,后面是一个个的方法,路径可以不传
    if (typeof path === "function") {
        handlers.unshift(path);
        path = "/";
    }
    // 如果是多个函数需要循环添加层
    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("请求到了");
    // 需要取出路由系统中 Router 存放的 layer 依次执行
    const { pathname } = url.parse(req.url);
    let idx = 0;
    let next = (err) => {
        // 遍历完后没有找到就直接走出路由系统
        if (idx >= this.stack.length) return out();
        let layer = this.stack[idx++];
        if (err) {
            console.log("统一对中间件跟路由错误处理");
            // 找错误处理中间件
            if (!layer.route) {
                // 如果是中间件自己处理
                layer.handle_error(err, req, res, next);
            } else {
                // 路由则跳过,继续携带错误向下执行
                next(err);
            }
        } else {
            // 需要判断 layer 上的 path 和当前请求路由是否一致,一致就执行 dispatch 方法
            if (layer.match(pathname)) {
                // 中间件没有方法可以匹配,不能是错误处理中间件
                if (!layer.route) {
                    if (layer.handler.length !== 4) {
                        layer.handle_request(req, res, next);
                    } else {
                        next();
                    }
                } else {
                    // 将遍历路由系统中下一层的方法传入
                    // 加速匹配,如果用户注册过这个类型的方法在去执行
                    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;
    }
    // 如果是中间件,进行中间件的匹配规则
    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) {
        // 调用错误处理中间件
        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 = [];
    // 用来描述内部存过哪些方法
    this.methods = {};
}

Route.prototype.dispatch = function (req, res, out) {
    // 稍后调用此方法时,回去栈中拿出对应的 handler 依次执行
    let idx = 0;
    console.log("this.stack----->", this.stack);
    let next = (err) => {
        // 如果内部迭代的时候出现错误,直接到外层的 stack 中
        err && out(err);
        // 遍历完后没有找到就直接走出路由系统
        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) => {
            // 这里的路径没有意义
            let layer = new Layer("/", handler);
            layer.method = method;
            // 做个映射表
            this.methods[method] = true;
            this.stack.push(layer);
        });
    };
});

module.exports = Route;

测试demo

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.coloradmin.cn/o/1038218.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023-09-23 Windows系统rust开发环境配置真经

Windows系统rust开发环境配置真经 前言一、配置C编译链和VsCode二、安装rust编译工具三、配置VsCode一. 安装rust-analyzer插件二. 安装Error Lens插件三. 安装Even Better TOML插件四. 配置 launch.json五. 配置 tasks.json六. 配置 Cargo.toml 总结 前言 有了配置C语言环境的…

腾讯mini项目-【指标监控服务重构】2023-08-27

今日已办 Docker Monitoring with cAdvisor, Prometheus and Grafana Docker Monitoring with cAdvisor, Prometheus and Grafana | by Mertcan Simsek | MediumMonitoring Docker container metrics using cAdvisor | Prometheus prometheus.yml global:scrape_interval: …

Linux下安装使用Redis

1.进入/usr/local目录&#xff0c;新建一个目录redis&#xff0c;下载redis安装包 wget https://download.redis.io/releases/redis-6.2.6.tar.gz**并解压 tar xzf redis-6.2.6.tar.gz进入redis-6.2.6目录 cd redis-6.2.6用make来编译 make2.启动redis: ./src/redis-server…

Vue-devTools安装—创建项目方法2 ui创建——Vue指令综合案例——汽车品牌管理

目录 项目源代码&#xff1a; 一、vue-devTools安装 二、案例功能实现 1、新建项目&#xff08;ui创建&#xff09; 2、cnpm导入项目依赖库 3、删除不需要的代码结构: 4、修改代码结构 5、添加汽车品牌 插件安装 bootstrap的提示功能 添加bootstrap样式 6、删除汽车品牌…

ndoe.js、npm相关笔记

1、npm 全局安装 npm config get prefix 获取 npm 全局安装路径如果全局插件不能正常使用&#xff0c;看环境变量是否已经配置。没有配置则把全局安装路径配置到环境变量的path中

【golang】调度系列之sysmon

调度系列 调度系列之goroutine 调度系列之m 调度系列之p 在golang的调度体系中&#xff0c;除了GMP本身&#xff0c;还有另外一个比较重要的角色sysmon。实际上&#xff0c;除了GMP和sysmon&#xff0c;runtime中还有一个全局的调度器对象。但该对象只是维护一些全局的数据&…

高云FPGA系列教程(10):letter-shell串口终端移植

文章目录 1. letter-shell简介2. letter-shell源码获取3. letter-shell移植4. 函数和变量应用示例本文是高云FPGA系列教程的第10篇文章。 shell,中文是外壳的意思,就是操作系统的外壳。通过shell命令可以操作和控制操作系统,比如Linux中的Shell命令就包括ls、cd、pwd等等。总…

leetcode100----双指针

283. 移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1:输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2:输入: nums …

英飞凌TC3xx--深度手撕HSM安全启动(六)--安全启动的TARA、HARA分析

在之前我们讲解基于Tricore的安全启动流程,但是是不是这种流程就是安全可靠的呢?不确定,因此对启动流程基于信息安全的TARA分析基和于功能安全的HARA分析必不可少。 1、安全启动的TARA分析 首先我们来看看什么叫做TARA分析。 在ISO\SAE 21434 中对于TARA描述为threat analy…

浅析工具dirpro v1.2源码

文章目录 前言源码分析dirpro.pystart.pybackup.pyrely.pyresults.pyend.py 前言 工具简介 dirpro 是一款由 python 编写的目录扫描器专业版&#xff0c;操作简单&#xff0c;功能强大&#xff0c;高度自动化 自动根据返回状态码和返回长度&#xff0c;对扫描结果进行二次整理…

【动手学深度学习-Pytorch版】注意力汇聚:Nadaraya-Watson 核回归

注意力机制中的查询、键、值 在注意力机制的框架中包含了键、值与查询三个主要的部分&#xff0c;其中键与查询构成了注意力汇聚&#xff08;有的也叫作注意力池化&#xff09;。 键是指一些非意识的线索&#xff0c;例如在序列到序列的学习中&#xff0c;特别是机器翻译&…

基于Python+Django的热门旅游景点数据分析系统的设计与实现(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

JAVA:实现Excel和PDF上下标

1、简介 最近项目需要实现26个小写字母的上下标功能,自己去网上找了所有Unicode的上下标形式,缺少一些关键字母,顾后面考虑自己创建上下标字体样式,以此来记录。 2、Excel Excel本身是支持上下标,我们可以通过Excel单元格的样式来设置当前字体上下标,因使用的是POI的m…

YOLOv5:修改backbone为ACMIX

YOLOv5&#xff1a;修改backbone为ACMIX 前言前提条件相关介绍ACMIXYOLOv5修改backbone为ACMIX修改common.py修改yolo.py修改yolov5.yaml配置 参考 前言 记录在YOLOv5修改backbone操作&#xff0c;方便自己查阅。由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评…

【软件设计师-从小白到大牛】上午题基础篇:第四章 法律法规与知识产权

文章目录 前言章节提要一、保护期限真题链接 二、知识产权人确定真题链接 三、侵权判定真题链接 四、标准化基础知识 前言 ​ 本系列文章为观看b站视频以及b站up主zst_2001系列视频所做的笔记&#xff0c;感谢相关博主的分享。如有侵权&#xff0c;立即删除。 视频链接&#xf…

Qt5开发及实例V2.0-第二十三章-Qt-多功能文档查看器实例

Qt5开发及实例V2.0-第二十三章-Qt-多功能文档查看器实例 第23章 多功能文档查看器实例23.1. 简介23.2. 界面与程序框架设计23.2.1. 图片资源23.2.2. 网页资源23.2.3. 测试用文件 23.3 主程序代码框架23.4 浏览网页功能实现23.4.1 实现HtmIHandler处理器 23.5. 部分代码实现23.5…

git 本地分支基础操作

&#xff08;1&#xff09;建立分支 a:基于某个commit建立分支 然后切换 git branch test_branch 6435675ad32c035ed4d9cb6c351de5cbaecddd99 git checkout test_branchb: git checkout 建立分支然后切换 git checkout -b checkout_branchc:分支建立 然后切换 git branch …

【Amazon】AI 代码生成器—Amazon CodeWhisperer初体验 | 开启开挂编程之旅

使用 AI 编码配套应用程序更快、更安全地构建应用程序 文章目录 1.1 Amazon CodeWhisperper简介1.2 Amazon CodeWhisperer 定价2.1 打开VS Code2.2 安装AWS ToolKit插件 一、前言 1.1 Amazon CodeWhisperper简介 1️⃣更快地完成更多工作 CodeWhisperer 经过数十亿行代码的训…

网络分层模型和常见协议介绍

文章目录 网络分层模型和常见协议介绍网络分层模型介绍常见各层协议介绍 网络分层模型和常见协议介绍 理解性记忆&#xff1a;这是我自己创造的一个理解性记忆口诀&#xff0c;大家别笑我&#x1f604; 七层&#xff1a;因为七层协议并没有得到应用&#xff0c;所以物&#xff…

【算法】相向双指针

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…