Лучший способ обрабатывать несколько функций / сценариев внутри экспресс-маршрута? - PullRequest
0 голосов
/ 23 октября 2018

У меня есть много конечных точек в моем экспресс-приложении, которые имеют много условий.Я хочу найти лучший шаблон дизайна для них, не повторяя при этом слишком много.

Это один из моих простых маршрутов:

 router.post('/reset/:token',
    asyncMiddleware(async(req, res, next) => { await reset(req, res, next, pino); })
  );

Внутри reset() Мне нужно проверитьпара вещей, например:

  • Если есть все необходимые параметры тела
  • Если электронное письмо с расшифрованным токеном совпадает с письмом из базы данных
  • Еслипароль был успешно сохранен.

Я хотел бы проверить те условия, которые не имеют огромной функции, но я не знаю, что лучший способ сделать это.

Весь код маршрута

export async function reset(req, res, next) {
  const email = req.body.email;
  if (!email) return res.status(400).json(Error.paramsMissing('email'));

  const user = await userAssociatedWithEmail(req.body.email);
  if (!user) {
    return res.status(501).json(Error.noActiveUserAssociatedWithEmail);
  }

  // Generate token
  const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');
  const emailSent = await sendForgotEmail(token, user);

  if (!emailSent) return res.status(500).json(Error.emailNotSent);
  else return res.json({ status: 'success', message: 'Email sent successfully.' });
}

Что я хотел бы сделать

Конечный результат Я хотел бы получить

export async function reset(req, res, next) {
  const email = req.body.email;
  if (!email) return res.status(400).json(Error.paramsMissing('email'));

  // If error inside userAssociatedWithEmail, I'd like to stop execution and 
  // return res.status(501).json(Error.noActiveUserAssociatedWithEmail) from inside 
  // that function, without having to add an if condition below as exists in the 
  // original code above
  const user = await userAssociatedWithEmail(req.body.email); 

  const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');

  // Again I'd like to return res.status(500).json(Error.emailNotSent) 
  // from inside sendForgotEmail IF there is an error
  const emailSent = await sendForgotEmail(token, user); 

  // If everything is successful, finally I'd return this
  return res.json({ status: 'success', message: 'Email sent successfully.' });
}

Объяснениерезультат в слове:

Я бы хотел иметь возможность обрабатывать условия и сценарии без необходимости обрабатывать их в основной функции reset, если это возможно (иначе, без необходимости хранить ответ в переменнойпроверьте переменную и return в основной функции в случае ошибки).

Так, например, вместо:

  const allParamsAreValid = validParams(token, email, new_password, res);
  if (!allParamsAreValid) return;

Я хотел бы сделать что-то вроде:

  validateParams(token, email, new_password, res);

А затем внутри validateParams(), если параметр отсутствует, я бы принудительно вышел из программы, кроме того, установив ответ с помощью res.json({}).

Возможно ли это?

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Вы можете заставить все ваши асинхронные функции, которые возвращают обещания, отклонять их обещание со статусом и значением, которое вы хотите отправить.Затем вы можете обработать это отклоненное обещание в одном месте:

export async function reset(req, res, next) {
    try {
        const email = req.body.email;
        if (!email) return res.status(400).json(Error.paramsMissing('email'));

        // If error inside userAssociatedWithEmail, I'd like to stop execution and 
        // return res.status(501).json(Error.noActiveUserAssociatedWithEmail) from inside 
        // that function, without having to add an if condition below as exists in the 
        // original code above
        const user = await userAssociatedWithEmail(req.body.email); 

        const token = await jwt.sign({ email: user.email, id: user.id }, 'shhhhh');

        // Again I'd like to return res.status(500).json(Error.emailNotSent) 
        // from inside sendForgotEmail IF there is an error
        const emailSent = await sendForgotEmail(token, user); 
        // If everything is successful, finally I'd return this
        res.json({ status: 'success', message: 'Email sent successfully.' });

    } catch(e) {
        res.status(e.status || 500).json(e.errData)
    }
}

И тогда все ваши асинхронные функции будут отклонены, если у них будет условие ошибки, и для обоих параметров будет установлено значение e.status и e.errDataпричина.Это позволило бы вам иметь один общий обработчик ошибок и позволить функции async собирать любые отклоненные обещания в ваш try / catch для вас.Это должен быть чистый способ обработки отклонений в серии вызовов await, в которых вы хотите завершить всю функцию.

Затем вам также необходимо убедиться, что ваша функция asyncMiddleware() НЕ такжеотправка ответа (не могу точно сказать, какова его цель).Вы не показываете этот код, поэтому я не вижу, что он делает.

Вы не показываете код, который использует validateParams(), но если он был синхронным, то он мог бы просто вызвать исключениес правильными полями, установленными на нем, и try/catch также будет ловить его так же, как и асинхронные отклонения.

Например:

function validateParams(token, email, new_password) {
    let err = new Error();
    err.errData = {status: 'error'};
    if (!token) {
       err.errData.message = 'invalid token';
       throw err;
    }
    if (!email) {
       err.errData = Error.paramsMissing('email');
       throw err;
    }
    if (!new_password) {
       err.errData.message = 'invalid new password');
       throw err;
    }
}

Если вы хотите, вы можететакже отправьте ответ об ошибке в validateParams(), но я думаю, что это чище, потому что они позволяют собирать все ошибки, включая все ваши await асинхронные вызовы, в одном try / catch в обработчике маршрутов, и, честно говоря, это намного более читабельнопонятный код не для отправки ответа в некоторых вызовах функций, но не в других.Я стараюсь, чтобы все мои ответы и об ошибках, и об успехе отправлялись на одном уровне.Тогда действительно легко отслеживать и избегать случайных попыток отправить несколько ответов.

Затем, в вашем обработчике маршрута, вы просто позвоните validateParams(...) просто так.Если он выдаст, ваш try / catch перехватит его и отправит соответствующую ошибку.Если ошибки нет, выполнение будет просто продолжено.

0 голосов
/ 23 октября 2018

Поскольку вы передаете объект res в метод validateParams , вы можете сделать что-то вроде этого,

async function validateParams(token, email, new_password, res) {
  if (token && emal && new_password &&  res) {
    // all values are available
    // perform your desired operation 
  } else {
    // exit from the method and pass info to the client
    return res.json({ message: 'Invalid parameter' });
  }
}

В этом случае все, что вам нужноdo вызывает validateParams.

await validateParams(token, email, new_password, res);

Если отсутствует параметр, сервер немедленно передает управление клиенту.В противном случае вы можете выполнить свою операцию там.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...