在同一请求中进行两次方法调用

时间:2017-09-03 23:37:50

标签: javascript node.js mongodb express mongoose

简要概述我要完成的任务:

在前端,用户可以使用表单编辑有关其帐户的信息。无论编辑/触摸了多少字段,该表单都会被发送到同一HTTP请求。

我的想法是请求将处理要编辑的字段。除了我不确定如何返回确认数据之外,这个逻辑对我来说似乎是合理的。例如,如何知道所有请求何时完成/要显示哪些确认和错误消息。

//Change information route
router.post('/changeinformation', passport.authenticate('jwt', {session: false}), (req, res, next) => {
    const changeInfo = {
        changeEmail: req.body.changeEmail,
        changeUsername: req.body.changeUsername
    };

    if(changeInfo.changeUsername === true) {
        const userInfo = {
            email: req.body.email,
            currentEmail: req.body.currentEmail
        };
        Artist.getArtistByEmail(userInfo.email, (err, user) => {
            if (err) throw err;
            if (user) {
                return res.json({success: false, msg: 'Email already exists'});
            }
            else {
                Artist.changeEmail(userInfo, (err, callback) => {
                    if (callback) {
                        console.log(callback);
                        return res.json({success: true, msg: 'Email has been changed successfully'});
                    }
                });
            }
        });
    }

    if(changeInfo.changeUsername === true) {
        //Checks if username exists
        const nameInfo = {
            name: req.body.name,
            currentName: req.body.currentName
        };
        Artist.getArtistByName(userInfo.name, (err, user) =>
        {
            if (err) throw err;
            if (user) {
                return res.json({success: false, msg: 'Name already exists'});
            }
            else
            {
                Artist.changeName(userInfo, (err, callback) => {
                    if(callback)
                    {
                        console.log(callback);
                        return res.json({success: true, msg: 'Name has been changed successfully'});
                    }
                });
            }
        });
    }
});

如果对某些人来说这看起来很明显,我道歉。我还在学习,不确定如何处理这个问题。感谢

2 个答案:

答案 0 :(得分:2)

在这里进行多次通话实际上并不需要,或者任何接近有效的地方。您可以根据所选选项构建查询并进行更新,而不是进行多次调用。

构造修改每个属性的不同方法也是完全没必要的。如果表达正确,标准的mongoose(实际上是真正的MongoDB)方法应该足够描述预期的功能。否则是麻烦和不切实际的,建立查询选项而不是特定的属性调用是你通常的意思:

router.post('/changeinformation',
  passport.authenticate('jwt', {session: false}), (req, res) => {

  let query = { },
      update = { $set: { } };

  if ( req.body.changeEmail ) {
    query['email'] = req.body.currentEmail;   // presuming this is current
    update.$set['email'] = req.body.email;
  }

  if ( req.body.changeUsername ) {
    query['name'] = req.body.currentName;
    update.$set['name'] = req.body.name;
  }

  // One call only
  User.update(query,update,(err,result) => {
    if (err) {
      if (err.code === 11000) {     // Duplicate key error
        res.json({ success: false, msg: 'email or name already exists' });
      } else {
        res.json({ success: false, msg: err.message });
      }
    } else {
      if ( result.n === 0 ) {
        res.json({ success: false, msg: 'Requested details not found' });
      } else {
        res.json({ success: true, msg: 'Details updated successfully' });
      }
    }

  });

});

因此,您只需检查提供的选项并从那里构建查询和更新语句,而不是分支和进行不同的调用。这些是“按设计”只是常规对象表示,就像您只是操纵结构来构造它们一样。

这里的第一个主要出发点是使用查询来“检查目标值是否存在”。这真的很低效。您真正要求的是这些值是“唯一的”,以便集合中的其他对象不能共享相同的nameemail。强制执行此操作的方法是使用“索引”:

const userSchema = new Schema({
  ...
  email: { type: String, required: true, unique: true },
  name: { type: String, required: true, unique: true }
  ...
});

模式中的那些"unique"属性告诉mongoose在该属性上创建一个“唯一”的索引。如果您尝试在集合中添加多个相同的值(即将用户名更改为与现有条目相同的值),那么这将抛出“重复键错误”,我们可以通过错误代码捕获做出相应的回应。

这就是代码的作用,通过测试返回的错误代码并在识别为重复键错误时给出相应的消息:

      if (err.code === 11000) {
        res.json({ success: false, msg: 'email or name already exists' });
      } else {
        res.json({ success: false, msg: err.message });
      }

这比制作单独的查询以测试目标值是否已存在更为直接。此外,它更可靠,因为在您的“查询”和当前代码中的后续“更新”之间,其他东西实际上可能会更改数据。因此,如果不强制执行唯一索引并检查错误,则存在创建重复项的风险。因此,使用正确的工具来完成正确的工作。

此处的其他检查当然是为了查看是否有任何内容与更新匹配。这始终包含在n属性下.update()的响应对象中,该属性表示条件实际匹配的文档数。

至于消息本身,您可以“按原样”保留它们,并且我发现个人非常合理,或者您可以在请求参数上使用类似的分支逻辑,就像语句结构一样,以确定要返回的特定消息内容

举个例子:

let errStr = (req.body.changeEmail && req.body.changeUsername)
  ? "email or name" 
  : ( req.body.changeEmail ) ? "email" : "name";
res.json({ success: false, msg: `${errStr} already exists` });

希望你能得到一般的想法。

但这比尝试将不同的呼叫链接在一起并在您真的不需要时发出多个请求要好得多。

作为旁注,我们实际上比使用回调嵌套有更现代和推荐的处理方式。 Prom已经存在了一段时间,你也应该在支持async / await语法的环境中工作,就像任何nodejs v8.xx版本一样,这实际上将成为长期的支持(LTS)版本:

router.post('/changeinformation',
  passport.authenticate('jwt', {session: false}), async (req, res) => {

  let query = { },
      update = { $set: { } };

  if ( req.body.changeEmail ) {
    query['email'] = req.body.currentEmail;   // presuming this is current
    update.$set['email'] = req.body.email;
  }

  if ( req.body.changeUsername ) {
    query['name'] = req.body.currentName;
    update.$set['name'] = req.body.name;
  }

  try {
    let result = await User.update(query,update);

    if ( result.n === 0 ) {
      let msgStr = (req.body.changeEmail && req.body.changeUsername)
        ? "email or name"
        : ( req.body.changeEmail ) ? "email" : "name";
      res.json({ success: false, msg: `Requested ${msgStr} not found` });
    } else {
      res.json({ success: true, msg: 'Details updated successfully' });
    }

  } catch(e) {
    if (e.code === 11000) {
      let errStr = (req.body.changeEmail && req.body.changeUsername)
        ? "email or name"
        : ( req.body.changeEmail ) ? "email" : "name";
      res.json({ success: false, msg: `${errStr} already exists` });
    } else {
      res.json({ success: false, msg: e.message });
    }
  }
});

当你确实有一系列异步函数需要解决时,以及我们在这个实例中我们真正想要避免的事情时,它确实会自成一体。但是,即使包含try..catch块的一般情况也至少具有更合理的流程,并且确实提高了代码的可读性。

答案 1 :(得分:1)

第一个if(changeInfo.changeUsername === true)应遵循您的逻辑if(changeInfo.changeEmail === true)