nodejs http.get响应中的body在哪里?

mik*_*ana 180 http node.js

我正在http://nodejs.org/docs/v0.4.0/api/http.html#http.request上阅读文档,但出于某种原因,我似乎无法真正找到body/data属性在返回的,已完成的响应对象上.

> var res = http.get({host:'www.somesite.com', path:'/'})

> res.finished
true

> res._hasBody
true
Run Code Online (Sandbox Code Playgroud)

它已经完成(http.get为你做了),所以它应该有某种内容.但是没有身体,没有数据,我无法从中读取.身体隐藏在哪里?

yoj*_*o87 167

http.request docs包含如何通过处理data事件接收响应体的示例:

var options = {
  host: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST'
};

var req = http.request(options, function(res) {
  console.log('STATUS: ' + res.statusCode);
  console.log('HEADERS: ' + JSON.stringify(res.headers));
  res.setEncoding('utf8');
  res.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

req.on('error', function(e) {
  console.log('problem with request: ' + e.message);
});

// write data to request body
req.write('data\n');
req.write('data\n');
req.end();
Run Code Online (Sandbox Code Playgroud)

http.gethttp.request做同样的事情,除了它req.end()自动调用.

var options = {
  host: 'www.google.com',
  port: 80,
  path: '/index.html'
};

http.get(options, function(res) {
  console.log("Got response: " + res.statusCode);

  res.on("data", function(chunk) {
    console.log("BODY: " + chunk);
  });
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});
Run Code Online (Sandbox Code Playgroud)

  • 出于某种原因,我不得不在http.get示例中添加`res.setEncoding('utf8');`.否则我没有在`chunk`变量中获得HTML. (14认同)
  • "data"事件可能会多次调用,您将逐个获取内容.示例未显示如何将它们粘合在一起. (13认同)
  • @tfmontague.同意!令人惊讶的是......如此多的赞成答案在其基础上存在缺陷. (4认同)

biz*_*izi 128

我还想补充说http.ClientResponse返回的http.get()有一个end事件,所以这是我收到身体响应的另一种方式:

var options = {
  host: 'www.google.com',
  port: 80,
  path: '/index.html'
};

http.get(options, function(res) {
  var body = '';
  res.on('data', function(chunk) {
    body += chunk;
  });
  res.on('end', function() {
    console.log(body);
  });
}).on('error', function(e) {
  console.log("Got error: " + e.message);
}); 
Run Code Online (Sandbox Code Playgroud)

  • 感谢那!"结束"事件对我来说至关重要,因为我必须整体处理响应主体,而不是整块. (12认同)

mik*_*ana 50

编辑:6年后回复自我

的await关键字是处理最好的办法,避免了回调和.then()

您还需要使用返回Promises的HTTP客户端. http.get()仍然返回一个Request对象,这样就无法工作了.您可以使用fetch,但它superagent是一个成熟的HTTP客户端,它具有更合理的默认值,包括查询更简单的字符串编码,正确使用mime类型,默认使用JSON以及其他常见的HTTP客户端功能.await将等到Promise有一个值 - 在这种情况下,HTTP响应!

const superagent = require('superagent');

(async function(){
  const response = await superagent.get('https://www.google.com')
  console.log(response.text)
})();
Run Code Online (Sandbox Code Playgroud)

使用await,一旦返回的promise 有一个值,control就会转到下一行superagent.get().

  • 这不能回答您的原始问题.在您的示例代码中,`res`设置为`superagent.get()`的返回值,而不是`http.get()`.`http.get()`返回一个没有`text`的[`http.IncomingMessage`](https://nodejs.org/docs/latest-v8.x/api/http.html#http_class_http_incomingmessage)属性.它不是响应对象,而是请求对象. (3认同)

小智 11

如果你想使用.get,你可以这样做

http.get(url, function(res){
    res.setEncoding('utf8');
    res.on('data', function(chunk){
        console.log(chunk);
    });

});
Run Code Online (Sandbox Code Playgroud)

  • 当我没有包含chunk响应的文本时,其他示例给了我看起来像十六进制值的内容.设置编码显示我正在寻找的JSON文档.谢谢! (2认同)

nkr*_*ron 11

data下载主体的"块"时会多次触发该事件,并且在下载end所有块时发生事件.

现在Node支持Promises,我创建了一个简单的包装器来通过Promise返回连接的块:

const httpGet = url => {
  return new Promise((resolve, reject) => {
    http.get(url, res => {
      res.setEncoding('utf8');
      let body = ''; 
      res.on('data', chunk => body += chunk);
      res.on('end', () => resolve(body));
    }).on('error', reject);
  });
};
Run Code Online (Sandbox Code Playgroud)

您可以通过以下方式从异步函数中调用它:

const body = await httpGet('http://www.somesite.com');
Run Code Online (Sandbox Code Playgroud)


Yak*_*ein 10

主体没有完全作为响应的一部分存储,这是因为主体可以包含非常大量的数据,如果将其存储在响应对象上,程序的内存会消耗得非常快。

相反,Node.js 使用一种称为Stream的机制。这种机制允许只保存一小部分数据,并允许程序员决定是在内存中完全使用它还是使用数据的每一部分作为其流动。

有多种方法可以将数据完全消耗到内存中,由于HTTP Response是可读流,因此res对象上的所有可读方法都可用

  1. 监听"data"事件并保存传递给回调的块

    const chunks = []
    
    res.on("data", (chunk) => {
        chunks.push(chunk)
    });
    
    res.on("end", () => {
        const body = Buffer.concat(chunks);
    });
    
    Run Code Online (Sandbox Code Playgroud)

使用这种方法时,您不会干扰流的行为,并且您只收集应用程序可用的数据。

  1. 使用"readble"事件并调用res.read()

    const chunks = [];
    
    res.on("readable", () => {
       let chunk;
       while(null !== (chunk = res.read())){
           chunks.push(chunk)
       }
    });
    
    res.on("end", () => {
        const body = Buffer.concat(chunks);
    });
    
    Run Code Online (Sandbox Code Playgroud)

使用这种方法时,您将完全负责流,直到res.read被调用时,才会将更多数据传递到流中。

  1. 使用异步迭代器

    const chunks = [];
    
    for await (const chunk of readable) {
        chunks.push(chunk);
    } 
    
    const body = Buffer.concat(chunks);
    
    Run Code Online (Sandbox Code Playgroud)

这种方法与方法类似"data" event。它只会简化范围,并允许整个过程在同一范围内发生。

虽然如上所述,可以完全使用响应中的数据,但如果确实有必要这样做,请始终牢记这一点。在许多情况下,可以简单地将数据定向到其目的地,而无需将其完全保存到内存中。

Node.js 读取流,包括 HTTP 响应,具有执行此操作的内置方法,该方法称为pipe。用法很简单,readStream.pipe(writeStream);.

例如:

如果您的数据的最终目的地是文件系统,您可以简单地打开一个写入文件系统的流,然后pipe将数据发送到 ts 目的地。

const { createWriteStream } = require("fs");
const writeStream = createWriteStream("someFile");
res.pipe(writeStream);
Run Code Online (Sandbox Code Playgroud)

  • 这很好地解释了为什么我在 VSC 调试器中看不到响应正文。谢谢你! (2认同)

Sko*_*ski 6

您需要为请求添加一个侦听器,因为node.js是异步的,如下所示:

request.on('response', function (response) {
  response.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
 });
});
Run Code Online (Sandbox Code Playgroud)