#javascript #node.js
#javascript #node.js
Вопрос:
Я создаю CronJob, который вызывает API и сохраняет его ответ в базе данных:
const CronJob = require("cron").CronJob;
const btc_price_ticker = require("../../controllers/BtcExchange/Ticker");
const currency = require("../../controllers/Currencies/CurrenciesController");
module.exports = new CronJob("* * * * * *", async function() {
const {
ticker: { sell }
} = await btc_price_ticker.getBtcPrice();
currency
.update({
params: {
id: "5cbdf078f5bcec257fcec792"
},
body: {
exchange_rate: sell,
lastUpdate: Date.now()
}
})
.catch(error => console.log(error));
});
Это работает нормально, однако я получаю TypeError: Cannot read property 'json' of undefined
Я использую ту же функцию для обновления базы данных, которую я использую при обновлении моим API:
module.exports = {
async update(req, res) {
const currency = await Currency.findByIdAndUpdate(req.params.id, req.body, {
new: true
});
return res.json(currency);
}
};
TypeError
Происходит в return res.json(currency)
, и это происходит только тогда, когда оно вызывается CronJob. Когда я добавляю новую информацию с помощью API, она не показывает никакой ошибки.
Я думаю, это происходит потому, что когда я вызываю функцию в CronJob
, я просто передаю req
параметр by, но я не знаю, как это решить. Что я должен делать?
Заранее спасибо!
Комментарии:
1. есть ли у вас выражение с использованием bodyparser
2. Вы вызываете
update()
без передачи объекта ответа в качестве второго аргумента3. Да, @alfasin, вы правы. Я почти уверен, что это ошибка, но я не знаю, что я должен передать в качестве объекта ответа
4. @OtavioBonder В идеале вы должны просто вызвать
Currency.findByIdAndUpdate
или любую другую распространенную функцию util в Corn, а не издеватьсяreq
иres
5. Если вы используете cron-job, зачем вам запрос / разрешение с самого начала?
Ответ №1:
Есть известное высказывание, в котором говорится, что вы можете решить практически любую проблему в CS, добавив еще один уровень косвенности. Это один из таких случаев:
Вместо объявления вашего модуля как:
module.exports = {
async update(req, res) {
const currency = await Currency.findByIdAndUpdate(req.params.id, req.body, {
new: true
});
return res.json(currency);
}
};
Отделите логику от логики маршрута:
module.exports = {
async getCurrency(id, params) {
const currency = await Currency.findByIdAndUpdate(id, params, {
new: true
});
return currency;
}
async update(req, res) {
const currency = await getCurrency(req.params.id, req.body);
return res.json(currency);
}
};
Теперь маршрут может вызывать update()
, а cron-задание может вызывать getCurrency()
напрямую.
Ответ №2:
Хорошо, возможно, мой подход не самый лучший, но поскольку я передаю неопределенный объект в update()
, и мне не нужен ответ, я отредактировал update
функцию:
async update(req, res) {
const currency = await Currency.findByIdAndUpdate(req.params.id, req.body, {
new: true
});
if (res !== undefined) {
return res.json(currency);
}
}
Итак, поскольку значение res не определено, оно ничего не возвращает.
Ответ №3:
Это потому, что оба req
и res
являются undefined
(или, по крайней мере, не являются надлежащим request
объектом response
), когда маршрут не запрашивается клиентом (что имеет место здесь).
Самое простое решение, о котором я могу подумать, — это имитировать (или выполнить фактический вызов) вызов клиента в вашей cron
работе, используя такие модули, как axios
, request
, request-promise
(с Promise
оболочкой поверх запроса), например:
const rp = require('request-promise')
module.exports = new CronJob("* * * * * *", async function() {
try {
const {
ticker: { sell }
} = await btc_price_ticker.getBtcPrice();
// assuming your server is running in localhost, port 3000 and route is 'myapi/:id'
const url = "http://localhost:3000/myapi/5cbdf078f5bcec257fcec792";
const options = {
method: 'GET',
uri : url,
json: true,
body: {
exchange_rate: sell,
lastUpdate: Date.now()
}
}
const response = await rp(url);
} catch(error) {
console.log(error);
}
});
Поскольку вы можете выполнять db
методы, более простой альтернативой было бы прямое выполнение Currency.findByIdAndUpdate
в вашем cron
задании. Я не вижу никаких причин, по которым вы хотели бы вызвать свой route
в своем коде.
Комментарии:
1. Это сделает
Corn
зависимым от сервера, а также приведет к некоторой задержке.2. Я не знаю варианта использования, но он использует
cron
противroute
. Я просто предоставляю решение для OP. Если бы это был я, я бы выполнил напрямуюCurrency.findByIdAndUpdate
Я не вижу никаких причин вызывать маршрут на вашем собственном сервере дляcron
3. Точно, это будет оптимальный подход.
4. Да, @naga-elixir-jar, ваш подход к прямому вызову
Currency.findByIdAndUpdate
намного лучше. Я буду реализовывать этот способ5. Что ж, тогда позвольте мне добавить это к ответу 🙂