Node.js

De Wiket
Salta a la navegació Salta a la cerca

volver a M06 Desarrollo web en entorno cliente

Como instalar Node.js en ubuntu 16.04

  • Para instalar node.js en ubuntu podemos hacerlo de varias maneras. La más rápida es instalarlo a través de los repositorios usando apt :
  1. Primero actualizamos nuestro repositorio
    usuario@miUbuntu:~$ sudo apt-get update
    
  2. Después instalamos los paquetes necesarios para crear paquetes
    usuario@miUbuntu:~$ sudo apt-get install build-essentials libssl-dev
    
  3. Ahora instalamos la apliación curl que nos permitirá conectarnos al servidor de nvm (node version manager) e instalarlo
    usuario@miUbuntu:~$ sudo apt-get install curl
    
  4. El siguiente paso es descargar y ejecutar el script de instalación de nvm
    usuario@miUbuntu:~$ curl https://raw.githubusercontent.com/creationix/nvm/v0.10.0/install.sh | sh
    
    • Ojo es posible que al ejecutar el comando anterior el script instalador no se ejecute, eso podría ser debido a que en se cree que sh es el alias de bash pero en ubuntu es el alias de dash por lo tanto si pasa esto tendremos que ejecutar el siguiente comando:
    usuario@miUbuntu:~$ curl https://raw.githubusercontent.com/creationix/nvm/v0.10.0/install.sh | PROFILE=~/.bashrc bash
    
  5. Ya estamos listos para usar nvm e instalar la versión de node.js que nos interese. Con el siguiente comando obtenemos la lista de todas las versiones disponibles de node.js
    usuario@miUbuntu:~$ nvm ls-remote
    
  6. En el momento de realizar este tutorial la versión estable de node.js es (v8.9.4) para instalarla solo tenemos que ejecutar lo siguiente:
    usuario@miUbuntu:~$ nvm install 8.9.4
    
  7. Para probar que tenemos node.js instalado y funcionando creamos un archivo (holaMundo.js) y escribimos:
    console.log("Hola Mundo");
    

    Guardamos el arvicho y luego ejecutamos con el siguiente comando:

    usuario@miUbuntu:~$ node holaMundo.js
    

    Si todo ha ido correctamente en nuestra consola debería aparecer el texto "Hola Mundo"

  8. NOTA: Si no funciona podemos probar a instalar nodejs-legacy
  • En este link hay otras maneras de instalar nodejs

Primeras aplicaciones en node.js

Creando un servidor HTTP básico con node.js

  1. Creamos un archivo (server.js), introducimos el siguiente código y lo guardamos
    • server.js
    //obtenemos el objeto http y lo guardamos en la variable http
    //require es una palabra reservada que sirve para cargar un módulo de node.js (en este caso el módulo http)
    var http = require("http");
    
    //usamos la función createServer del objeto http para crear el servidor que atenderá las peticiones
    http.createServer(function(request, response) {
      //writeHead es una función para crear la cabecera de respuesta HTTP
      response.writeHead(200, {"Content-Type": "text/html"});
      //write es una función para enviar el cuerpo de la respuesta (en este caso "Hola Mundo")
      response.write("Hola Mundo");
      //end es la función que indica que ya hemos terminado la respuesta
      response.end();
    //ejecutamos la función listen del objeto que devuelve la función createServer para indicar que puerto escuchará el servidor (en este caso 8888)
    }).listen(8888);
    
    • También podemos refactorizar el código anterior para no usar funciones anónimas:
      • server.js
      var http = require("http");
      
      function onRequest(request, response) {
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("Hola Mundo");
        response.end();
      }
      
      http.createServer(onRequest).listen(8888);
      
  2. Ahora ejecutamos el archivo
    usuario@miUbuntu:~$ node server.js
    
  3. Por último abrimos un navegador y accedemos a http://localhost:8888/ y deberíamos ver el texto "Hola Mundo" en el navegador

Transformar nuestro servidor en un Módulo

  • Hasta ahora tenemos un código que tenemos que arrancar nosotros manualmente y accediendo a la URL http://localhost:8888/ nos contesta con "Hola Mundo" ya que esa es la funcionalidad actual de nuestro servidor. Pero en realidad sería interesante poder arrancar nuestro servidor desde una aplicación externa (index.js por ejemplo) cuando vayamos a necesitar pedirle cosas al servidor.
  • Ahora lo que vamos a hacer es transformar nuestro servidor (server.js) en un módulo que puede ser llamado desde otra aplicación.
  1. Para transformar nuestro servidor en un módulo modificaremos el código de server.js de la siguiente manera:
    • server.js
    var http = require("http");
    
    //la función iniciar encapsula la función onRequest que se encarga de atender las peticiones al servidor
    function iniciar() {
      function onRequest(request, response) {
        console.log("Petición Recibida.");
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("Hola Mundo");
        response.end();
      }
    
      http.createServer(onRequest).listen(8888);
      console.log("Servidor Iniciado.");
    }
    //exports nos permite definir que funciones será accesibles (públicas) para otras aplicaciones
    //en este caso exportamos la función iniciar por lo tanto desde otro código js podemos utilizar la función iniciar de nuestro módulo server
    exports.iniciar = iniciar;
    
  2. Ahora creamos un archivo con el nombre index.js e introducimos el siguiente código:
    • index.js
    var server = require("./server");
    
    server.iniciar();
    

    Esto nos permitirá obtener el módulo server y arrancar el servidor

  3. Por último arrancamos nuestro servidor haciendo uso de nuestro script principal de este modo:
    usuario@miUbuntu:~$ node index.js
    

Enrutando peticiones al servidor

  • Cuando se hacen peticiones a un servidor, según la URL, este debe de saber contestar de manera adecuada. Ahora modificaremos nuestro código para que el servidor sea capaz de responder según la URL de la petición
  1. Vamos a crear un archivo (router.js) que se va a encargar de enrutar las peticiones. Creamos el archivo e introducimos el siguiente código:
    • router.js
    function route(pathname) {
      console.log("A punto de rutear una peticion para " + pathname);
    }
    
    exports.route = route;
    
  2. Ahora tenemos que conseguir que el servidor pueda usar la función route de nuestro router, para esto usaremos el fichero index.js para cargar el módulo router y a la función iniciar le pasaremos como parámetro la función route para que el servidor pueda hacer uso de ella.
    • index.js
    var server = require("./server");
    var router = require("./router");
    
    server.iniciar(router.route);
    
  3. Por último modificamos el archivo server.js para que la función iniciar reciba un parámetro que será la función route y la usaremos dentro del servidor
    • server.js
    var http = require("http");
    //cargamos el módulo url para poder parsear la url enviada en el request
    var url = require("url");
    
    //añadimos el parámetro route que corresponde a la función route del módulo router
    function iniciar(route) {
      function onRequest(request, response) {
        //obtenemos el pathname la request haciendo uso de las funciones públicas (exportadas) del módulo url
        var pathname = url.parse(request.url).pathname;
        console.log("Peticion para " + pathname + " recibida.");
        
        //usamos la función route que ha sido pasada por parámetro
        route(pathname);
    
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("Hola Mundo");
        response.end();
      }
    
      http.createServer(onRequest).listen(8888);
      console.log("Servidor Iniciado.");
    }
    
    exports.iniciar = iniciar;
    

    En este punto tenemos un que recibe una request y se la pasa al router para que haga lo que sea con ella. No es muy funcional (de momento) pero nos permite observar de que manera podemos tratar las peticiones y que podemos manejarlas de manera independiente.

  4. El siguiente paso es crear un módulo que nos permita gestionar/manejar estas peticiones para este propósito crearemos un fichero (requestHandlers.js) e introduciremos el siguiente código:
    • requestHandlers.js
    function iniciar() {
      console.log("Manipulador de petición 'iniciar' ha sido llamado.");
    }
    
    function subir() {
      console.log("Manipulador de petición 'subir' ha sido llamado.");
    }
    
    exports.iniciar = iniciar;
    exports.subir = subir;
    

    Según la URL de la request se ejecutará la función que corresponda. De esta manera si introducimos la URL http://localhost:8888/iniciar la idea es que el código de la función iniciar del módulo requestHandlers se ejecute.

  5. Añadir más rutas a nuestra aplicación es algo que va a suceder. Por lo tanto esa acción no debería ser costosa a nivel de desarrollo por lo tanto vamos a implementar un sistema para que en caso de tener que añadir una nueva ruta sea lo más sencillo posible. Por ejemplo que solo tengamos que añadir la ruta en un único fichero. Modificaremos el fichero index.js para hacer esto:
    • index.js
    var server = require("./server");
    var router = require("./router");
    //pedimos el módulo requestHandlers creado en el apartado anterior
    var requestHandlers = require("./requestHandlers");
    
    //usaremos un pseudoarrayasociativo (key-value pair) donde la key será el pathname y el valor la función del módulo requestHandlers que se tiene que ejecutar
    var handle = {
       "/" : requestHandlers.iniciar,
       "/iniciar" : requestHandlers.iniciar,
       "/subir" : requestHandlers.subir
       };
    
    //le pasamos la rutas a manejar al servidor a través de la función iniciar
    server.iniciar(router.route, handle);
    
  6. Modificamos el servidor para que la función iniciar reciba y trate el nuevo parámetro
    • server.js
    var http = require("http");
    var url = require("url");
    
    //la función iniciar recibe el parámetro handle (array pseudoasociativo que relaciona el pathname con una función del módulo requestHandlers)
    function iniciar(route, handle) {
      function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        console.log("Peticion para " + pathname + " recibida.");
    
        //añadimos el parámetro handler a la función route del módulo router
        route(handle, pathname);
    
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("Hola Mundo");
        response.end();
      }
    
      http.createServer(onRequest).listen(8888);
      console.log("Servidor Iniciado.");
    }
    
    exports.iniciar = iniciar;
    
  7. Solo nos queda modificar el módulo router para que la función route envíe la petición al manejador adecuado
    • router.js
    function route(handle, pathname){
       console.log("A punto de rutear una petición para " + pathname);
       //Dado que handle contiene como key el pathname y como valor la función del módulo requestHandlers asociada con esta sintaxis ejecutamos directamente la función adecuada según el pathname
       handle[pathname]();
    }
    
    exports.route = route;
    
  8. Finalmente podemos probar la ejecución de nuestro código lanzando index.js así:
    usuario@miUbuntu:~$ node index.js
    

    y si accedemos a la URL http://localhost:8888/iniciar el resultado debería de ser:

    --Servidor Iniciado--
    Petición Recibida
    A punto de rutear una petición para /iniciar
    Manejador de petición 'iniciar' ha sido llamado.
    

    si queremos probar con la otra URL http://localhost:8888/subir el resultado debería de ser:

    --Servidor Iniciado--
    Petición Recibida
    A punto de rutear una petición para /subir
    Manejador de petición 'subir' ha sido llamado.
    

Manejando peticiones POST

Para finalizar con esta primera aproximación a Node.js vamos a modificar nuestro código para presentar un área de texto que pueda ser rellenada por el usuario y luego enviada al servidor en una petición POST. Después mostraremos el texto recibido.

  1. Vamos a modificar nuestra función iniciar para que muestre un HTML muy simple que nos provea de un formulario para poder enviar la petición junto a los datos introducidos por el usuario.
    • resquestHandlers.js
    function iniciar(response) {
      console.log("Manipulador de peticiones 'iniciar' fue llamado.");
    
      var body = '<html>'+
        '<head>'+
        '<meta http-equiv="Content-Type" content="text/html; 
            charset=UTF-8" />'+
        '</head>'+
        '<body>'+
        '<form action="/subir" method="post">'+
        '<textarea name="text" rows="20" cols="60"></textarea>'+
        '<input type="submit" value="Enviar texto" />'+
        '</form>'+
        '</body>'+
        '</html>';
    
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write(body);
        response.end();
    }
    
    function subir(response) {
      console.log("Manipulador de peticiones 'subir' fue llamado.");
      response.writeHead(200, {"Content-Type": "text/html"});
      response.write("Hola Subir");
      response.end();
    }
    
    exports.iniciar = iniciar;
    exports.subir = subir;
    
  2. Tal como hemos visto, una cosa importante es procurar que los procesos no sean bloqueantes. Por eso mismo Node.js le entrega la información POST en pequeños trozos llamados (Callbacks) que se ejecutan ante determinados eventos. (data = llega un trozo de información) (end = todos los trozos de información han llegado). Nosotros le diremos a node.js que funciones tiene que ejecutar cuando estos eventos ocurran. De esta manera modificaremos el fichero de server.js de la siguiente manera:
    • server.js
    var http = require("http");
    var url = require("url");
    
    function iniciar(route, handle) {
      function onRequest(request, response) {
            var dataPosteada = "";
            var pathname = url.parse(request.url).pathname;
            console.log("Peticion para " + pathname + " recibida.");
    
            request.setEncoding("utf8");
    
            request.addListener("data", function(trozoPosteado) {
              dataPosteada += trozoPosteado;
              console.log("Recibido trozo POST '" + trozoPosteado + "'.");
        });
    
        request.addListener("end", function() {
          route(handle, pathname, response, dataPosteada);
        });
    
      }
    
      http.createServer(onRequest).listen(8888);
      console.log("Servidor Iniciado");
    }
    
    exports.iniciar = iniciar;
    
  3. Lo siguiente será modificar router.js para recibir el nuevo parámetro (dataPosteada) para que la función subir pueda pintar la información recibida. De modo que quedaría de esta manera:
    • router.js
    function route(handle, pathname, response, postData) {
      console.log("A punto de rutear una peticion para " + pathname);
      if (typeof handle[pathname] === 'function') {
        handle[pathname](response, postData);
      } else {
        console.log("No se ha encontrado manipulador para " + pathname);
        response.writeHead(404, {"Content-Type": "text/html"});
        response.write("404 No encontrado");
        response.end();
      }
    }
    
    exports.route = route;
    
  4. Modificamos la función subir para que reciba la información pasada por POST pueda pintarla
    *'''requestHandlers.js'''
    function iniciar(response, postData) {
      console.log("Manipulador de Peticion 'iniciar' fue llamado.");
    
      var body = '<html>'+
        '<head>'+
        '<meta http-equiv="Content-Type" content="text/html; 
            charset=UTF-8" />'+
        '</head>'+
        '<body>'+
        '<form action="/subir" method="post">'+
        '<textarea name="text" rows="20" cols="60"></textarea>'+
        '<input type="submit" value="Enviar texto" />'+
        '</form>'+
        '</body>'+
        '</html>';
      
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write(body);
        response.end();
    }
    
    function subir(response, dataPosteada) {
      console.log("Manipulador de Peticion 'subir' fue llamado.");
      response.writeHead(200, {"Content-Type": "text/html"});
      response.write("Tu enviaste: " + dataPosteada);
      response.end();
    }
    
    exports.iniciar = iniciar;
    exports.subir = subir;
    

    Y con esto deberíamos de tener la aplicación funcionando...

Ejercicios

  1. Dado el siguiente código modifícalo para que al cargar la URL http://localhost:8888/iniciar el resultado en pantalla sea "Hola has ejecutado el código asociado al manejador 'iniciar' y al cargar la URL http://localhost:8888/subir el resultado en pantalla sea "Hola has ejecutado el código asociado al manejador 'subir'. Tienes que hacerlo devolviendo (comando return) el resultado desde la función del manejador (requestHandlers.iniciar/ requestHandlers.subir) y ese resultado mandarlo como el contenido del response en la función iniciar del módulo server.
    • index.js
    var server = require("./server");
    var router = require("./router");
    var requestHandlers = require("./requestHandlers");
    
    var handle = {
       "/" : requestHandlers.iniciar,
       "/iniciar" : requestHandlers.iniciar,
       "/subir" : requestHandlers.subir
       };
    
    server.iniciar(router.route, handle);
    
    • router.js
    function route(handle, pathname){
       console.log("A punto de rutear una petición para " + pathname);
    
       handle[pathname]();
    }
    
    exports.route = route;
    
    • server.js
    var http = require("http");
    var url = require("url");
    
    function iniciar(route, handle) {
      function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        console.log("Peticion para " + pathname + " recibida.");
    
        route(handle, pathname);
    
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("Hola Mundo");
        response.end();
      }
    
      http.createServer(onRequest).listen(8888);
      console.log("Servidor Iniciado.");
    }
    
    exports.iniciar = iniciar;
    
    • requestHanderls.js
    function iniciar() {
      console.log("Manipulador de petición 'iniciar' ha sido llamado.");
    }
    
    function subir() {
      console.log("Manipulador de petición 'subir' ha sido llamado.");
    }
    
    exports.iniciar = iniciar;
    exports.subir = subir;
    
  2. Modifica la función iniciar del módulo requestHandlers de la siguiente manera:
    function iniciar() {
      console.log("Manejador de peticion 'iniciar' fue llamado.");
    
      function sleep(milliSeconds) {  
        // obten la hora actual
        var startTime = new Date().getTime();
        // atasca la cpu
        while (new Date().getTime() < startTime + milliSeconds); 
      }
    
      sleep(10000);
      return "Hola Iniciar";
    }
    
    • Explica que hace el nuevo código
    • Sigue estos pasos y luego explica el comportamiento del código:
      1. Abre dos ventanas del navegador
      2. En la primera introduce en la url: http://localhost:8888/iniciar PERO SOBRETODO NO LA EJECUTES AUN!!!
      3. En la segunda introduce en la url: http://localhost:8888/subir IGUAL QUE EN EL CASO ANTERIOR NO LA EJECUTES TODAVÍA!!
      4. Ahora lo más rápido que puedas ejecuta la primera url y luego la segunda
    • Busca en El libro para principiantes de Node.js la respuesta para aprender sobre el concepto de bloqueante/ no bloqueante