Как отправить окончательный ответ от обратного вызова findOne()?

У меня есть контроллер пользователя, в котором есть метод создания, который проверяет базу данных на уникальность электронной почты и имени пользователя перед созданием пользователя (это для обхода ошибки в mongodb adpater для SailsJS, который не учитывает флаг уникального атрибута — версия 0.10 .5).

Код выглядит следующим образом:

User.find({ email: req.body.email }, function (err, user) {
  if(user) {
    return res.badRequest('Unique email constraint. Email is already used.');
  }
});

User.create(req.body).exec(function (err, user) {
// Code to catch and manage err or new user
}

Я ожидаю, что если электронное письмо уже существует в базе данных (mongodb), для отправки 400 с помощью res.badRequest(), а затем выполнение будет завершено.

Что происходит, так это то, что ответ отправляется, но затем управление переходит к User.create() - выполнение не заканчивается. Я подозреваю, что return res.badRequest возвращает управление вызывающей функции (User.findOne), и выполнение продолжается оттуда.

Я попытался использовать res.badRequest().end(), но это оставило клиент в зависании (ответа нет), а использование res.end() после возврата res.badRequest() сгенерировало ошибки «отправки заголовка».

Как завершить выполнение этого запроса, если найдено существующее электронное письмо?


person onblur    schedule 01.10.2014    source источник


Ответы (1)


Прежде всего, ваш findOne здесь find. Это не связано с вашей проблемой, но немного сбивает с толку, и вы должны убедиться, что получаете данные в ожидаемом формате.

Что касается завершения запроса после того, как он помечен как плохой, я не использовал паруса, но в прошлом я мог завершить выполнение с помощью res.send(). РЕДАКТИРОВАТЬ: после просмотра документов кажется, что это сделано для вас .badRequest(), поэтому игнорируйте эту часть.

Тем не менее, даже ЭТО на самом деле не ваша проблема. Ваша проблема заключается в том, что вы запускаете асинхронный User.find(), а затем немедленно запускаете User.create() (тоже асинхронно), поэтому ваш запрос не будет помечен как плохой до тех пор, пока после вы не выполните уже пытался создать нового пользователя.

Что вам нужно сделать, это одно из двух:

  1. Используйте обещания (ПРИМЕЧАНИЕ: так это работает для Mongoose; Sails могут отличаться ), чтобы запускать User.create() только после завершения User.find(). например;

    var userQuery = User.findOne({ email: req.body.email }).exec();
    userQuery.addBack(function(err, user) {
        if(!!user) res.badRequest('...');
        else create_user();
    });
    
  2. Поместите логику создания пользователей внутри блока findOne. например.;

    User.findOne({ email: req.body.email }, function(err, user) {
        if (user) { // or perhaps you want if (!err)
            User.create(...);
        } else {
            // handle error
        }
    });
    

Лично я бы посоветовал вам использовать промисы (особенно позже, когда у вас есть длинные цепочки запросов, происходящих один поверх другого), но выбирайте сами.

person Jordan    schedule 01.10.2014
comment
Промисы уже встроены в Waterline (Sails ORM), в настоящее время используется Q, поэтому вы можете выполнять User.findOne(...).then(...) и User.findOne(...).catch(...). Но в большинстве случаев ошибки не будут выдаваться запросами, если только что-то плохое не произойдет на уровне базы данных, поэтому даже при использовании промисов вы всегда будете помещать всю свою логику в обработчик then. Однако ваше второе предложение совершенно справедливо. - person sgress454; 02.10.2014
comment
Спасибо, Джордан. Я попробую это - я подозреваю, что вы правы. - person onblur; 02.10.2014
comment
@ sgress454, я не знал о Waterline. Однако приведенное выше верно для Mongoose - я должен указать это предостережение в ответе. - person Jordan; 02.10.2014
comment
Только что попробовал вариант 2 и теперь работает по назначению. Кроме того, я изменил User.find() с .findOne(), когда возился с этим, чтобы устранить неполадки, но забыл установить его обратно - он должен быть findOne (хотя, я думаю, вы могли бы использовать find(), а затем проверить длину массива, но findOne работает нормально). - person onblur; 02.10.2014