En esta lección necesitamos escribir un programa que recoja todos los datos de una petición HTTP GET y registre el número de caracteres y la cadena completa de caracteres recibidos del servidor. ¿Pero no lo hicimos en el último ejercicio? No exactamente. El módulo http emite eventos a medida que se procesa la solicitud.

Leccion-8 learnyounode Lección 8 – HTTP Collect

En el ejercicio anterior usamos el método.on() para escuchar el primer evento de datos que recibimos. Todos los demás eventos son ignorados. Echa un vistazo de nuevo a la solución oficial de la lección anterior.

var http = require(‘http’)
 
http.get(process.argv[2], function (response) {
  response.setEncoding(‘utf8’)
  response.on(‘data’, console.log)
  response.on(‘error’, console.error)
}).on(‘error’, console.error)  

Si se mira de cerca, se puede ver que sólo estamos actuando sobre dos acontecimientos. Si obtenemos un evento de datos, estamos registrando esos datos en la consola. Si obtenemos un evento de error, estamos registrando el error en la consola. No hay nada en nuestro código que nos permita actuar más allá de estos eventos. Si hay más eventos de datos que puedan ocurrir, no estamos haciendo nada para actuar sobre esos eventos.

Obtenemos un par de consejos de learnyounode sobre cómo recopilar todos los datos que se envían desde el servidor en lugar de sólo el primer evento. La primera pista es que podemos usar el evento final para determinar cuándo hemos recibido todos los datos. La segunda pista es que podemos aprovechar algunos módulos de nodos existentes desde npm para ayudarnos a resolver este problema, como bl y concat-stream.

Solución oficial

var http = require(‘http’)var bl = require(‘bl’) http.get(process.argv[2], function (response) {  response.pipe(bl(function (err, data) {    if (err)      return console.error(err)    data = data.toString()    console.log(data.length)    console.log(data)  }))})

En la solución oficial se utiliza el módulo buffer list (bl). La respuesta de la petición GET es canalizada al método bl usando el método .pipe(). La lista de buffer acepta una llamada de retorno como argumento y expone los datos en el objeto buffer con el objeto buffer del nodo principal. No es necesario utilizar el módulo de lista tampón para completar esta tarea. Sin embargo, vale la pena saberlo porque el módulo de lista de buffer incluye un número de métodos prototipo útiles en su API como bl.get() que devuelve bytes en el índice especificado y bl.slice() que devuelve un nuevo objeto buffer con bytes en el rango especificado. Estos métodos son útiles para manipular flujos de búfer. Para obtener más información sobre el módulo de lista de búferes, consulte la documentación de la API de módulos en getHub.

Otra solución

Si tiene curiosidad por ver cómo se puede resolver este problema sin utilizar un módulo de nodo adicional, consulte la solución alternativa a continuación.

var http = require(‘http’)
var url = process.argv[2]
var body = »
 
http.get(url, function (response) {
  response.on(‘data’, function (chunk) {
    body += chunk
  })
  response.on(‘end’, function () {
    console.log(body.length)
    console.log(body)
  })
})

La solución alternativa añade el cuerpo de respuesta a la variable de cuerpo cuando ocurre un evento de datos. Cuando el método response.on(‘end’) encuentra el evento final, el cuerpo y la longitud del cuerpo se registran en la consola.

Vale la pena mencionar que el uso de los eventos http es también una gran manera de evitar quedarse atrapado en un conjunto de llamadas de retorno profundamente anidadas. ¿Notas cómo se asigna el cuerpo var fuera del método asíncrono http.get? Cada vez que obtenemos un evento de datos, concatenamos la siguiente parte de la respuesta al cuerpo. Entonces, cuando se emite el evento final, sabemos que la operación asíncrona está completa y podemos registrar el contenido del cuerpo en la consola. Inténtelo usted mismo ejecutando el nodo http-collect.js http://google.com en la línea de comandos. Debería ver algo como lo siguiente en la línea de comandos:

219
<HTML><HEAD><meta http-equiv=»content-type» content=»text/html;charset=utf-8″>
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF=»http://www.google.com/»>here</A>.
</BODY></HTML>

Compara esto con la salida del comando curl ejecutando $ curl -v http://google.com.

* Rebuilt URL to: http://google.com/
*   Trying 2607:f8b0:4005:809::200e…
*   Trying 172.217.6.46…
* Connected to google.com (172.217.6.46) port 80 (#0)
> GET / HTTP/1.1
> Host: google.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Location: http://www.google.com/
< Content-Type: text/html; charset=UTF-8
< Date: Wed, 24 Jan 2018 16:19:28 GMT
< Expires: Fri, 23 Feb 2018 16:19:28 GMT
< Cache-Control: public, max-age=2592000
< Server: gws
< Content-Length: 219
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: SAMEORIGIN
<
<HTML><HEAD><meta http-equiv=»content-type» content=»text/html;charset=utf-8″>
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF=»http://www.google.com/»>here</A>.
</BODY></HTML>
* Connection #0 to host google.com left intact

Puedes ver que el cuerpo y la longitud del contenido son los mismos. ¿Qué pasa si intentas registrar el cuerpo fuera de la devolución de llamada?.

var http = require(‘http’)
var url = process.argv[2]
var body = »
 
http.get(url, function (response) {
  response.on(‘data’, function (chunk) {
    body += chunk
  })
  response.on(‘end’, function () {
  })
})
 
console.log(body.length)
console.log(body)

Bueno, no obtienes nada…

Por qué? La respuesta es tiempo. http.get() se ejecuta primero. Empieza un trabajo asincrónico. Entonces se ejecutan ambas sentencias console.log(). Pero, todo esto sucede antes de que obtengamos una respuesta http de vuelta, así que nada se concatena con el cuerpo antes de registrar su contenido. De hecho, después de ejecutar el último console.log(), la cola de mensajes está vacía y el programa sale. Node.js no espera a que se añadan mensajes asíncronos a la cola de mensajes antes de salir.

¿Qué pasa si todavía queremos pasar los resultados de http.get() a otra función? Piensa en hacerlo modular. Apliquemos el mismo pensamiento envolviendo http.get() con otra función que acepte una devolución de llamada, y luego devolviendo esa devolución de llamada en la función anónima que pasamos a response.on(‘end’).

var http = require(‘http’)
var url = process.argv[2]
var body = »
 
var getBody = function (callback) {
  http.get(url, function (response) {
    response.on(‘data’, function (chunk) {
      body += chunk
    })
    response.on(‘end’, function () {
      return callback()
    })
  })
}
 
getBody(function () {
  console.log(body.length)
  console.log(body)
})

Como puedes ver, podemos usar la llamada de retorno para coordinar cuando registramos el contenido del cuerpo con el evento final de nuestra respuesta http. Hay otras formas de pasar datos asíncronos. Eche un vistazo a este artículo para aprender a pasar datos asíncronos con promesas.

Reader Interactions

Deja un comentario

Tu dirección de correo electrónico no será publicada.

About David Moya

Apasionado de la Seguridad Informática, aprendiendo en todo momento y profundizando en el mundo web y en el posicionamiento en buscadores.

Share This