在Express应用中有条件地发送响应

3

我想知道在Express应用程序中,是否可以编写if语句来有条件地执行代码而不提供else语句。

if(pred) {
  doSomething()
}
return foo;
calcBar(); // doesn't run.

上面是同步代码,在return语句后停止执行。

我的Express函数如下:

app.get('/matches', async function(req, res) {
  try {
    const data = await someGraphQLCall();
    if(data.length === 0) {
      res.json({ message: "No data." });
    }
    const someOtherData = await someOtherGraphQLCall(data.foo);
    res.json({ someOtherData });
  } catch (err) {
    res.json({err})
  }
}

这个问题所述,第一个res.json之后的代码可能仍会被执行。有没有一种方法可以停止它?如果第一个if条件得到满足,我不想执行第二个GraphQL调用。是否有可能不使用else来实现呢?

编辑:

如上述链接所提到的那样,使用return语句是一个糟糕的选择,因为:

它也让其变得含义不明确和模糊,因为它使用了错误的语义。如果您没有使用函数的返回值,则不应该返回它。


在我看来,这是一个糟糕的理由。如果你的REST API没有返回数据,那么最好向用户抛出404错误,然后继续执行代码。返回语句是编程中如此基本的块,以至于它不会模糊其作用。而且,你的返回将返回什么?显然,它将返回用户请求的数据,你还能使用更精确的语句吗?我认为,在单个if语句中包装和隐藏这些内容是模糊的,也是一个可怕的解决方案。你可以使用它来检查像if (data.hasSomeData) fetchFromGQL这样的东西。 - Thomas Johansen
3个回答

7
您可以在第一个响应上使用return关键字,以立即从函数中返回。
app.get('/matches', async function(req, res) {
  try {
    const data = await someGraphQLCall();
    if(data.length === 0) {
      return res.json({ message: "No data." });
    }
    const someOtherData = await someOtherGraphQLCall(data.foo);
    res.json({ someOtherData });
  } catch (err) {
    res.json({err})
  }
} 
编辑:

作为替代方法,您可以将数据逻辑和构建响应的过程分开。这样,您就可以使用“return”,并且代码更易于阅读:

app.get('/matches', async function (req, res) {
    try {
        const data = await getDataFromGraphQLCall();
        res.json(data);
    } catch (err) {
        res.json({ err })
    }
});

async function getDataFromGraphQLCall() {
    const data = await someGraphQLCall();
    if (data.length === 0) {
        return { message: "No data." };
    }
    const someOtherData = await someOtherGraphQLCall(data.foo);
    return { someOtherData };
}

谢谢Oresztresz。我知道我可以使用else,但我想知道是否有不使用else的可能性。而且我不想使用return语句(我已经在问题中编辑了原因)。 - J. Hesters
你是否希望在下一个语句中返回?res.json({ message: "No data." }); return; 在我看来,守卫子句是众所周知且易读的。请参见:https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html - Oresztesz

2
如果你想在没有使用else的情况下实现这个目标,是可以做到的。但这可能不是最简洁的方法。在我看来,使用return命令终止控制器的执行是最好的方法。
无论如何,你都可以将代码块拆分为中间件,并使用三元运算符有条件地发送响应信息。
在你的例子中,可以按照以下方式将data = await someGraphQLCall();分离出来:
const middlewareOne = async function(req, res, next) {
    let data = [];
    let response = { message: "No data." };
    try {
        data = await someGraphQLCall();
        req.locals.data = data; // <- attach the data to req.locals
    } catch (err) {
        response = { err };
    }
    data.length === 0 ? res.json(response) : next();
};

然后,在你的控制器之前挂载middlewareOne中间件

app.get("/matches", middlewareOne, async function controller(req, res) {
    try {
        const someOtherData = await someOtherGraphQLCall(req.locals.data.foo);
        res.json({ someOtherData });
    } catch (err) {
        res.json({ err });
    }
});

这是如何运作的:只有在前一个中间件(例如上面的middlewareOne)调用了next()后,express才会执行controller函数。
由于middlewareOne仅在data.length不为0时调用next(),所以它将按照您的预期工作。
要了解有关从一个中间件传递数据到另一个中间件的更多信息,请阅读此文档

哇,谢谢。诚然,使用else比较好。 - J. Hesters
app.get()是否可以访问在middlewareOne中使用let声明的数据?因为你需要将其作为下一个GraphQL调用的输入,使用data.foo - J. Hesters
你想从 middlewareOne 访问数据到 controller 吗? - Anand Undavia
我想在 app.get 中访问它。 - J. Hesters

0

return语句在此上下文中终止函数执行。我认为,您应该先处理成功情况,然后再处理错误情况,因为代码将从上到下读取。

if语句中,数据可能是undefinednull

您可以在这里阅读更多信息:MDN - return

app.get('/matches', async function(req, res) {
  try {
    const data = await someGraphQLCall();

    // alternative, if (data && data[0]) {
    if (data && data.length) {
      const someOtherData = await someOtherGraphQLCall(data.foo);
      return res.json({ someOtherData });
    }

    return res.json({ message: "No data." });
  } catch (err) {
    console.log(err); // log error with logger and drain to loggly.
    res.json({ err })
  }
} 

使用Void运算符:
Void运算符允许您返回未定义的值,但会评估给定的表达式。
您可以在这里阅读更多信息:MDN - Void
app.get('/matches', async function(req, res) {
  try {
    const data = await someGraphQLCall();

    // alternative, if (data && data[0]) {
    if (data && data.length) {
      const someOtherData = await someOtherGraphQLCall(data.foo);
      return void res.json({ someOtherData });
    }

    return void res.json({ message: "No data." });
  } catch (err) {
    console.log(err); // log error with logger and drain to loggly.
    res.json({ err })
  }
}

谢谢你,akinjide。我不想使用 return 语句(我已经通过编辑问题说明了原因)。 - J. Hesters
嗨,海斯特,那是一个有效的理由,你应该看一下void运算符。 - Akinjide

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接