nodejs - 如何宣传http.request?拒绝被叫了两次

hap*_*set 39 httprequest node.js promise

我试图http.request融入Promise:

 new Promise(function(resolve, reject) {
    var req = http.request({
        host: '127.0.0.1',
        port: 4000,
        method: 'GET',
        path: '/api/v1/service'
    }, function(res) {
        if (res.statusCode < 200 || res.statusCode >= 300) {
            // First reject
            reject(new Error('statusCode=' + res.statusCode));
            return;
        }
        var body = [];
        res.on('data', function(chunk) {
            body.push(chunk);
        });
        res.on('end', function() {
            try {
                body = JSON.parse(Buffer.concat(body).toString());
            } catch(e) {
                reject(e);
                return;
            }
            resolve(body);
        });
    });
    req.on('error', function(err) {
        // Second reject
        reject(err);
    });
    req.write('test');
}).then(function(data) {
    console.log(data);
}).catch(function(err) {
    console.log(err);
});
Run Code Online (Sandbox Code Playgroud)

如果我statusCode从远程服务器接收到错误,它将调用第一次拒绝,并在一段时间后第二次拒绝.如何正确使它只调用单一拒绝(我认为在这种情况下,首先拒绝是正确的)?我想我需要关闭res自己,但对象close()上没有方法ClientResponse.

UPD: 第二次拒绝很少触发 - 为什么?

dan*_*anh 60

你的代码几乎没问题.要重做一点,你需要一个用这个表单包装http.request的函数:

function httpRequest(params, postData) {
    return new Promise(function(resolve, reject) {
        var req = http.request(params, function(res) {
            // on bad status, reject
            // on response data, cumulate it
            // on end, parse and resolve
        });
        // on request error, reject
        // if there's post data, write it to the request
        // important: end the request req.end()
    });
}
Run Code Online (Sandbox Code Playgroud)

注意加入的paramspostData所以这可以用作一个通用的请求. 并注意 req.end() OP代码中缺少最后一行 - 必须始终被调用.

将这些夫妻变化应用于OP代码......

function httpRequest(params, postData) {
    return new Promise(function(resolve, reject) {
        var req = http.request(params, function(res) {
            // reject on bad status
            if (res.statusCode < 200 || res.statusCode >= 300) {
                return reject(new Error('statusCode=' + res.statusCode));
            }
            // cumulate data
            var body = [];
            res.on('data', function(chunk) {
                body.push(chunk);
            });
            // resolve on end
            res.on('end', function() {
                try {
                    body = JSON.parse(Buffer.concat(body).toString());
                } catch(e) {
                    reject(e);
                }
                resolve(body);
            });
        });
        // reject on request error
        req.on('error', function(err) {
            // This is not a "Second reject", just a different sort of failure
            reject(err);
        });
        if (postData) {
            req.write(postData);
        }
        // IMPORTANT
        req.end();
    });
}
Run Code Online (Sandbox Code Playgroud)

这是未经测试的,但应该可以正常工作......

var params = {
    host: '127.0.0.1',
    port: 4000,
    method: 'GET',
    path: '/api/v1/service'
};
// this is a get, so there's no post data

httpRequest(params).then(function(body) {
    console.log(body);
});
Run Code Online (Sandbox Code Playgroud)

这些承诺也可以链接......

httpRequest(params).then(function(body) {
    console.log(body);
    return httpRequest(otherParams);
}).then(function(body) {
    console.log(body);
    // and so on
});
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案。我是 nodejs 和异步处理的新手,但我正在尝试执行一系列 HTTP 请求。所有这些请求都应该是有序的,我不能使用任何外部库。但是,我不确定您的回答是否允许按顺序运行多个 HTTP 请求。可以? (2认同)
  • @Slav,当然.我编辑以在最后说明. (2认同)

Dee*_*pak 11

还有其他方法,但在这里您可以找到一种简单的方法将 http.request 设为 promise 或 async/await 类型。

这是一个工作示例代码:

var http = require('http');

function requestAsync(name) {

    return new Promise((resolve, reject) => {
        var post_options = {
            host: 'restcountries.eu',
            port: '80',
            path: `/rest/v2/name/${name}`,
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        };
        let post_req = http.request(post_options, (res) => {
            res.setEncoding('utf8');
            res.on('data', (chunk) => {
                resolve(chunk);
            });
            res.on("error", (err) => {
                reject(err);
            });
        });
        post_req.write('test');
        post_req.end();
    });
}

//Calling request function
//:1- as promise
requestAsync("india").then(countryDetails => {
    console.log(countryDetails);
}).catch((err) => {
    console.log(err);  
}); 

//:2- as await
let countryDetails = await requestAsync("india");
Run Code Online (Sandbox Code Playgroud)


Nic*_*tte 5

我知道这个问题很旧,但是答案实际上激发了我编写轻量级承诺HTTP客户端的现代版本。这是一个新版本:

  • 使用最新的JavaScript语法
  • 验证输入
  • 支持多种方法
  • 易于扩展以支持HTTPS
  • 让客户决定如何处理响应码
  • 还将让客户端决定如何处理非JSON主体

代码如下:

function httpRequest(method, url, body = null) {
    if (!['get', 'post', 'head'].includes(method)) {
        throw new Error(`Invalid method: ${method}`);
    }

    let urlObject;

    try {
        urlObject = new URL(url);
    } catch (error) {
        throw new Error(`Invalid url ${url}`);
    }

    if (body && method !== 'post') {
        throw new Error(`Invalid use of the body parameter while using the ${method.toUpperCase()} method.`);
    }

    let options = {
        method: method.toUpperCase(),
        hostname: urlObject.hostname,
        port: urlObject.port,
        path: urlObject.pathname
    };

    if (body) {
        options.headers['Content-Length'] = Buffer.byteLength(body);
    }

    return new Promise((resolve, reject) => {

        const clientRequest = http.request(options, incomingMessage => {

            // Response object.
            let response = {
                statusCode: incomingMessage.statusCode,
                headers: incomingMessage.headers,
                body: []
            };

            // Collect response body data.
            incomingMessage.on('data', chunk => {
                response.body.push(chunk);
            });

            // Resolve on end.
            incomingMessage.on('end', () => {
                if (response.body.length) {

                    response.body = response.body.join();

                    try {
                        response.body = JSON.parse(response.body);
                    } catch (error) {
                        // Silently fail if response is not JSON.
                    }
                }

                resolve(response);
            });
        });

        // Reject on request error.
        clientRequest.on('error', error => {
            reject(error);
        });

        // Write request body if present.
        if (body) {
            clientRequest.write(body);
        }

        // Close HTTP connection.
        clientRequest.end();
    });
}
Run Code Online (Sandbox Code Playgroud)


Thi*_*ven 5

阅读所有这些文章和几篇文章后,我想我应该发布一种处理 http 和 https 的“通用”解决方案:

const http = require("http");
const https = require("https");
const url_obj = require("url");

const request = async (url_string, method = "GET", postData = null) => {
  const url = url_obj.parse(url_string);
  const lib = url.protocol=="https:" ? https : http;
  const params = {
    method:method,
    host:url.host,
    port: url.port || url.protocol=="https:" ? 443 : 80,
    path: url.path || "/"
  };
  return new Promise((resolve, reject) => {
    const req = lib.request(params, res => {
      if (res.statusCode < 200 || res.statusCode >= 300) {
        return reject(new Error(`Status Code: ${res.statusCode}`));
      }
      const data = [];
      res.on("data", chunk => {
        data.push(chunk);
      });
      res.on("end", () => resolve(Buffer.concat(data).toString()));
    });
    req.on("error", reject);
    if (postData) {
      req.write(postData);
    }
    req.end();
  });
}
Run Code Online (Sandbox Code Playgroud)

你可以这样使用:

request("google.com").then(res => console.log(res)).catch(err => console.log(err))
Run Code Online (Sandbox Code Playgroud)

这很大程度上受到本文的启发,但用内置的 api 替换了 hacky url 解析。


Fer*_*rin -4

使用 bluebird api 更容易,您可以promisify request 模块并使用 request 函数 async 作为 Promise 本身,或者您可以选择使用模块request-promise,这使您不必创建 Promise 而是使用以及已经使用 Promise 封装模块的对象,这是一个示例:

var rp = require('request-promise');

rp({host: '127.0.0.1',
    port: 4000,
    method: 'GET',
    path: '/api/v1/service'})
    .then(function (parsedBody) {
        // GET succeeded... 
    })
    .catch(function (err) {
        // GET failed... 
    });
Run Code Online (Sandbox Code Playgroud)

  • @happy_marmoset - 一旦被拒绝,承诺就完成了。如果您期望它报告第二个错误,那么您的设计方向就错误了。如果您需要两个单独的错误,那么您将需要两个单独的承诺。整个承诺模型是,一旦出现错误,它就会拒绝并且不再执行该特定的异步操作。调用者可以从 Promise 外部“处理”拒绝并决定下一步要做什么,但异步操作的结果(Promise 返回的内容)是在它拒绝时完成的。 (2认同)
  • 自 2020 年 2 月 11 日起,“request”及其依赖项“request-promise”和“request-promise-native”已弃用。 (2认同)