Модуль NodeJS https создает функцию многократного использования для динамической отправки запроса и обработки ответа

Этот вопрос похож на этот старый вопрос, но мне не удалось заставить принятый ответ работать правильно.

Я использую встроенный модуль NodeJS 'https' для отправки запросов к внешнему API. NodeJS версия 12.

node: 12.16
express: 4.16.1

Я смог заставить его работать с примером кода из документации.

router.get('/', (req, res, next) => {
  const requestOptions = httpCtrl.getReqOptions();

  // Working example
  // How to separate this logic into reusable function?
  const request = https.request(requestOptions, (response) => {
    let result = {
      status: null,
      data: {}
    };

    let rawData = ''
    response.on('data', (chunk) => {
      rawData += chunk;
    });

    response.on('end', () => {
      console.log('No more data in response.');
      try {
        parsedData = JSON.parse(rawData);
        result.status = parsedData.status || 200;
        result.data = parsedData;
        return res.status(result.status).json(result);
      } catch (e) {
        result.status = 500;
        result.data.message = `ERROR: Unable to parse API response`;
        result.data.exception = e;
        return res.status(result.status).send(result);
      }
    });

  });

  request.on('error', (e) => {
    result.status = 500;
    result.data.message = `ERROR: API response`;
    result.data.exception = e;
    return res.status(result.status).send(result);
  });
  request.end();
});

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

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

httpCtrl = {};

httpCtrl.createRequest = (requestOptions) => {

  // Does not work due to being synchronous, also tried with async await to no avail
  const request = https.request(requestOptions, (response) => {
    let result = {
      status: null,
      data: {}
    };

    let rawData = ''
    response.on('data', (chunk) => {
      rawData += chunk;
    });

    response.on('end', () => {
      console.log('No more data in response.');
      try {
        parsedData = JSON.parse(rawData);
        result.status = parsedData.status || 200;
        result.data = parsedData;
        return result;
      } catch (e) {
        result.status = 500;
        result.data.message = `ERROR: Unable to parse NRS Admin API response`;
        result.data.exception = e;
        return result;
      }
    });

  });

  request.on('error', (e) => {
    result.status = 500;
    result.data.message = `ERROR: API response`;
    result.data.exception = e;
    return result;
  });
  request.end();
});

}

router.get('/', (req, res, next) => {

  const requestOptions = httpCtrl.setRequestOptions();
  const result = httpCtrl.createRequest(requestOptions);
  return res.status(result.status).send(result);

});

Как я могу обновить этот код, чтобы сделать его более пригодным для повторного использования?

Всего 1 ответ


Преобразуйте функцию createRequest в обещание , обещания работают как обратные вызовы, за исключением того, что они намного лучше читаются.

// *** createReuqest function is a Promise ***
httpCtrl.createRequest = (requestOptions) => {
  return new Promise((resolve, reject) => {
    const result = {};
   // *** http.request function is a Callback ***
    const request = http.request(requestOptions, response => {
      let rawData = ''
      response.on('data', chunk => rawData += chunk);
      // resolve the promise when response ends
      response.on('end', () => {
        result.status = response.status || 200;
        result.data = rawData;
        resolve(result);
      });
    });
    // or reject on error
    request.on('error', e => {
      result.status = 500;
      result.data = {
        message: 'ERROR: API response',
        exception: e
      };
      reject(result);
    });
    request.end();
  });
};

Теперь мы просто вызываем функцию и связываем ее с помощью then и catch , однако я решил использовать async / await для включения всего асинхронного JavaScript в этом примере :) async / await основан на обещаниях, но с еще более чистой разметкой.

// *** Finally async/await ***
router.get('/', async (req, res) => {
  // initial options for testing
  const requestOptions = {
    hostname: 'www.google.com',
    port: 443,
    method: 'GET'
  };
  // await must be in try/catch to properly handle promise's resolve/reject
  try {
    const response = await httpCtrl.createRequest(requestOptions);
    res.status(response.status).send(response.data);
  } catch (error) {
    res.status(error.status).send(error.data);
  }
});

Надеюсь, я помог.