express的实现 | 5.错误处理

835 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

王志远,微医前端技术部

先看应用

应用规则:

  • 首先进行错误中间件注册(错误中间件区别普通中间件就在于它有四个参数)
app.use((err,req,res,next) => {
    res.header('Content-Type','text/html;charset=utf-8');
    res.end(err)
})
  • 应用时,只要在执行next函数时传递参数,就会执行到错误中间件的回调中,并且会将值传递给err
app.use(function (req,res,next){
    let flag = Math.random() > 0.5;
    if(flag){
        return next('出错了');
    }
    next();
})

访问效果如下

实现思路

同样的,对错误中间件进行区别处理,判断逻辑

  1. 根据route属性的有无判断是不是中间件
  2. 根据fn.length是不是 4 判断是不是错误中间件 (函数的length属性是参数个数)

具体实现

改写 Router 的handle方法,注意两点:

  1. 如果 next 有参数,则查找错误中间件以执行
  2. 在执行中间件时要进行判断,从而避免执行了错误中间件
Router.prototype.handle = function(req, res, done) {
    let { pathname } = url.parse(req.url);
    let method = req.method.toLowerCase()
    let idx = 0;
    let removed = '';
    const next = (err) => { // 中间件 和内部的 next 方法 出错都会走这个 next
        if (idx >= this.stack.length) return done(); // 路由处理不了 传递给应用层
        let layer = this.stack[idx++];
        if (err) {
            // 如果有错误 , 找错误处理中间件
            if(!layer.route){ // 中间件
                if(layer.handler.length === 4){
                    layer.handler(err,req,res,next)
                }else{
                    next(err);
                }
            }else{ // 路由
                next(err);
            }
        } else {
            // 无论是路由还是中间件 前提是路径必须匹配
            if (layer.match(pathname)) { // match 还没有更改
                if (!layer.route) { // 没有说明是中间件
                    // 正常中间件不走错误
                    if(layer.handler.length !== 4){
                        layer.handle_request(req, res, next); // 直接执行中间件函数
                    }else{
                        next();
                    }
                } else {
                    // 路由必须匹配方法
                    if (layer.route.methods[method]) { // 这个 next 可以让路由层扫描下一个 layer
                        layer.handle_request(req, res, next); // route.dispatch
                    } else {
                        next();
                    }
                }
            } else {
                next();
            }
        }
    }
    next(); // 请求来了取出第一个执行

}