为什么当参数较少时回调函数会起作用?

Why does callback function work when there are fewer arguments?

提问人:Siddharth Potti 提问时间:8/5/2022 最后编辑:LayhoutSiddharth Potti 更新时间:8/6/2022 访问量:184

问:

法典

有人可以解释为什么在函数 verifyUser 中,函数 verifyToken 被传递回调函数作为第四个参数时,此代码有效。但是如果我们看一下 verifyToken 函数,它只有 3 个参数/参数。这是如何工作的????

import jwt from "jsonwebtoken";
import { createError } from "../utils/error.js";

export const verifyToken = (req, res, next) => {
    const token = req.cookies.access_token;
    if (!token) {
        return next(createError(401, "You are not authenticated!"));
    }
    //Verifying if token is correct
    //Error (err) or token data (user) are returned by verify function
    jwt.verify(token, process.env.JWT, (err, user) => {
        if (err) return next(createError(403, "Token is not valid!"));
        req.user = user;
        next();
    });
};

export const verifyUser = (req, res, next) => {
    verifyToken(req, res, next, () => {
        if (req.user.id === req.params.id || req.user.isAdmin) {
            next();
        } else {
            return next(createError(403, "You are not authorized!"));
        }
    });
};
JavaScript Express Web 回调 后端

评论

1赞 jfriend00 8/5/2022
这没有任何意义。 将调用良好(带有一个额外的参数),但 NOTHING in 不会调用作为第四个参数传递的回调。如果您认为这真的发生了,那么请提供一个最小的、可重现的示例,我们可以自己运行和调试,因为显然除了我们在这里看到的代码之外,还有其他事情正在发生。verifyUser()verifyToken()verifyToken()
0赞 jfriend00 8/5/2022
我的理论是,您传递给的回调永远不会被调用。您的代码似乎可以工作,因为调用会继续路由并使事情看起来可以正常工作。您的测试可能永远不会运行。您可以通过在该回调中放置一个来验证这一点。verifyToken()verifyToken()next()if (req.user.id === req.params.id || req.user.isAdmin)console.log("in my callback passed to verifyToken()");
0赞 jfriend00 8/6/2022
你要去哪里?我们正在努力帮助和澄清问题,而您不是来回应的。如果您不打算在这里澄清事情,我们无法有效地提供帮助。
0赞 Siddharth Potti 8/6/2022
问题已在下面得到解答
0赞 jfriend00 8/6/2022
那么,你是否同意你的第四个参数回调永远不会被执行?

答:

0赞 Brother58697 8/5/2022 #1

我去挖掘Express的源代码,找到了这个文档

调用函数后,就像中间件一样 它将继续执行路由或后续参数函数。next()

因此,看起来首先尝试调用回调参数(如果它已传递),然后尝试调用下一个中间件。next()

我认为他们这样做的原因,是在不改变他们的实现的情况下硬编码或链接对之前的调用,这些实现在调用时会跳转到下一个中间件而不是继续。verifyTokenverifyUsernext()verifyTokenverifyUser

我写了一个示例脚本,在这里使用了相同的想法:

function printResult(object, next) {
  console.log("The final results is: " + object.result);
  next();
}

function printLog(object, next){
  console.log('Running printLog')
  console.log('The current result is: ' + object.result)
  next();
}

function add5(object, next) {
  // Chain printLog before add5 so that it always runs before it
  printLog(object, next, () => {
    console.log('Now running add5')
    object.result = object.result + 5;
    next();
  })
}


function addOne(object, next) {
  console.log('Running addOne')
  object.result = object.result + 1;
  next();
}

function callStack(stack, object) {

  function next(index, object) {
    return function runNext() {
       let caller = runNext.caller
       if(caller.name !== '' && caller.arguments[2]) {
          caller.arguments[2]()
       } else if (stack[index + 1]){
          stack[index + 1](object, next(index + 1, object));
       }
     }  
   }
   
  stack[0](object, next(0, object)); 
}

const stack = [addOne, add5, printResult]
const object = {result: 0}

callStack(stack, object)

请注意,存在匿名函数检查,因为检查匿名函数的 arguments 属性会引发错误。caller.name !== ''

更全面地解释为什么这很重要。

因此,如果您的应用程序是这样的,则没有问题:

  • 中间件 1
    • next()转到下一个中间件
  • verifyToken(中间件 2)
    • next()转到下一个中间件
  • 中间件 3

但是如果是这样的,没有调用参数函数:next()

  • 中间件 1
    • next()转到下一个中间件
  • verifyUser(中间件 2)
    • 调用verifyToken
      • next()in 转到下一个中间件 <- 这是一个问题verifyToken
    • 继续< - 因为这永远不会发生verifyUser
    • next()转到下一个中间件
  • 中间件 3

因此,他们让调用一个参数函数:next()

  • 中间件 1
    • next()转到下一个中间件
  • verifyUser(中间件 2)
    • 使用 的实现作为回调进行调用(例如,使用别名)verifyTokenverifyUserveryifyCB
      • next()in 检查回调,查找并调用它verifyTokenverifyCB
    • next()在转到下一个中间件verifyCB
  • 中间件 3

评论

0赞 Siddharth Potti 8/6/2022
是的!这正是我想要的!