Prácticas

Para las siguientes prácticas se indicarán los siguientes puntos a desarrollar por cada una:

  • Cada práctica contará con su propio directorio ráiz: $HOME/www/fetch0x
  • Cada práctica contará con un nombre de host: fetch0x.com
  • Cada práctica contará con un puerto TCP: 808x
  • En base al nombre de host se tendrá un URL: http://fetch0x.com:808x
  • El servidor web a usar es thttpd:

     alumno@servidor:~ $ cd $HOME/www/fetch0x
     alumno@servidor:~ $ thttpd -D -p 808x -T UTF-8 -nor -l -
    

Para configurar el nombre de host tienes que asociar la dirección ip 127.0.0.1 a fetch0x.com (según sea el caso) en el archivo /etc/hosts del sistema operativo.

Práctica 01

Esta práctica plantea la descarga de un documento XML haciendo uso de fetch().

  1. De http://www.sepomex.gob.mx/lservicios/servicios/CodigoPostal_Exportar.aspx descarga los códigos postales de algún estado en formato XML. Te recomiendo que trabajes con Baja California Sur ya que contiene menos cantidad de códigos postales.

    Notas:

    • En el caso de Baja California Sur el archivo que entrega el servidor solo tiene por nombre Baja, descargalo y renombralo a BajaCaliforniaSur.xml.
    • En base al contenido de los archivos en formato XML, estos han sido generados desde Microsoft SQL Server mediante el uso de DataSets (ADO.NET).
  2. Crea el directorio xml y en él deposita el archivo descargado en el anterior paso.

  3. Crea el archivo index.html con el siguiente contenido:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>fetch01</title>
        <style>
          body {
            width: 960px;
            margin: 10px auto 30px auto;
          }
          #archivo_a_descargar {
            width: 100%;
          }
          #archivo_descargado {
            width: 100%;
            height: 200px;
          }
          #mensaje {
            color: red;
          }
        </style>
      </head>
      <body onload="iniciar();">
        <h1>fetch01</h1>
          <form id="formulario">
            <fieldset>
              <legend>Entidades</legend>
                <label for="archivo">Seleccione una entidad:</label>
                <select id="archivo_a_descargar">
                  <option value="Aguascalientes.xml">Aguascalientes</option>
                  <option value="BajaCalifornia.xml">Baja California</option>
                  <option value="BajaCaliforniaSur.xml">Baja California Sur</option>
                  <option value=""></option>
                  <option value="Oaxaca.xml">Oaxaca</option>
                  <option value="EntidadQueNoExiste.xml">Entidad Que No Existe</option>
                  <option value="Zacatecas.xml">Zacatecas</option>
                </select>
                <input type="button" value="Descargar" id="descargar" />
            </fieldset>
          </form>
        <h1>Resultado:</h1>
        <p id="mensaje"></p>
        <textarea id="archivo_descargado"></textarea>
      <script src="js/fetch01a.js"></script>
      </body>
    </html>
    
  4. Crea el directorio js y en él el archivo fetch01a.js con el siguiente contenido:

    let descargar = (evento) => {
      let archivo_a_descargar = document.getElementById("archivo_a_descargar");
      let archivo_descargado = document.getElementById("archivo_descargado");
      let mensaje = document.getElementById("mensaje");
      archivo_descargado.value = "";
      mensaje.textContent = "";
      if (! /^\w+\.xml$/.test(archivo_a_descargar.value)) {
         mensaje.textContent = "Seleccione una entidad.";
         return false;
      }
      let servidor = "http://fetch01.com:8081";
      let url = servidor + "/xml/" + archivo_a_descargar.value + "?r=" + Math.random();
      let cabeceras_http = new Headers();
      let opciones_solicitud_http = {
        "method": "GET",
        "headers": cabeceras_http,
        "mode": "cors",
        "cache": "default"
      };
      let solicitud_http = new Request(url, opciones_solicitud_http);
      fetch(solicitud_http)
        .then((objeto_respuesta) => {
         let archivo_descargado = document.getElementById("archivo_descargado");
         let mensaje = document.getElementById("mensaje");
         archivo_descargado.value = "";
         mensaje.textContent = "";
         if (objeto_respuesta.status === 200) {
           objeto_respuesta.text().then((texto) => {
             archivo_descargado.value = texto;
           });
         } else {
           mensaje.textContent = "Error " + objeto_respuesta.status + " " + objeto_respuesta.statusText + " - " + objeto_respuesta.url;
         }
        })
        .catch((objeto_error) => {
          mensaje.textContent = "Error " + objeto_error.name + " : " + objeto_error.message;
        });
    };
    let iniciar = () => {
      let boton_descargar = document.getElementById("descargar");
      boton_descargar.addEventListener('click', descargar, false);
    };
    
  5. Accede a http://fetch01.com:8081 y mientras tengas abierto a Console selecciona a Baja California Sur del formulario mostrado y da clic sobre el botón Descargar.

Descargua los códigos postales de los demás estado siguiendo la misma pauta dada para Baja California Sur, tomando en cuenta de no usar letras acentuadas en el nombre de los archivos, y actualiza el elemento <select> encontrado en el archivo index.html.

Al finalizar tendrás una estructura similar a:

alumno@servidor:~/www/fetch01 $ pwd && tree
/home/alumno/www/fetch01
.
├── index.html
├── js
│   └── fetch01a.js
└── xml
    ├── Aguascalientes.xml
    ├── BajaCaliforniaSur.xml
    ├── BajaCalifornia.xml
    ├── ...
    ├── VeracruzDeIgnacioDeLaLlave.xml
    ├── Yucatan.xml
    └── Zacatecas.xml
2 directories, 34 files

Práctica 02

Esta práctica plantea el recorrido de un documento XML así como acceder a su contenido y generar a partir de él una tabla de HTML (<table>).

  1. Crea el archivo index.html el siguiente contenido:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>fetch02</title>
        <style>
          body {
            width: 960px;
            margin: 10px auto 30px auto;
          }
          #archivo_a_descargar {
            width: 100%;
          }
          #archivo_descargado {
            width: 100%;
            height: 200px;
          }
          #mensaje {
            color: red;
          }
          #resultado {
            width: 100%;
            font-family: sans-serif;
            color: #000;
            font-size: 7px;
            background: #ccc;
            margin: 0;
            border: #aaa 1px solid;
          }
          #resultado tr {
            text-align: center;
          }
          #resultado th {
            border-top: 1px solid #fafafa;
            border-bottom:1px solid #e0e0e0;
            background: #ededed;
            text-align: center;
          }
          #resultado td {
            border-top: 1px solid #ffffff;
            border-bottom:1px solid #e0e0e0;
            border-left: 1px solid #e0e0e0;
            background: #fafafa;
            text-align: center;
          }
          #resultado tr.even td {
            background: #f6f6f6;
          }
          #resultado tr:hover td {
            background: #f2f2f2;
          }
        </style>
      </head>
      <body onload="iniciar();">
        <h1>fetch02</h1>
          <form id="formulario">
            <fieldset>
              <legend>Entidades</legend>
                <label for="archivo">Seleccione una entidad:</label>
                <select id="archivo_a_descargar">
                  <option value="Aguascalientes.xml">Aguascalientes</option>
                  <option value="BajaCalifornia.xml">Baja California</option>
                  <option value=""></option>
                  <option value="EntidadQueNoExiste.xml">Entidad Que No Existe</option>
                  <option value="BajaCaliforniaSur.xml">Baja California Sur</option>
                  <option value=""></option>
                  <option value="Zacatecas.xml">Zacatecas</option>
                </select>
                <input type="button" value="Descargar" id="descargar" />
            </fieldset>
          </form>
        <h1>Resultado: <span id="cantidad_de_codigos_postales"></span></h1>
        <p id="mensaje"></p>
        <table id="resultado"><thead></thead><tbody></tbody></table>
      <script src="js/fetch02a.js"></script>
      </body>
    </html>
    
  2. Crea el archivo js/fetch02a.js con el siguiente contenido:

    let actualizarTabla = (documento_xml) => {
      /* Para acceder al DOM de documento_xml en Console: */
      console.log("Documento: " + documento_xml.URL);
      console.log(documento_xml);
      /* Eliminación de thead y tbody de table */
      let tabla = document.getElementById("resultado");
      if (tabla.firstChild && tabla.lastChild) {
        tabla.removeChild(tabla.firstChild);
        tabla.removeChild(tabla.lastChild);
      }
      /* Cabecera (<thead>) de la tabla
       * El siguiente documento PDF indica los campos que conforman la información
       * por cada estado:
       * http://www.correosdemexico.gob.mx/lservicios/servicios/imagenes%5CDescrip.pdf
       */
      let campos = [];
      let xml_campos_primer_estado = documento_xml.firstChild.childNodes[1].childNodes;
      let tabla_thead = document.createElement("thead");
      tabla.appendChild(tabla_thead);
      let tabla_tr = document.createElement("tr");
      tabla_thead.appendChild(tabla_tr);
      let tabla_th = document.createElement("th");
      tabla_th.textContent = "#";
      tabla_tr.appendChild(tabla_th);
      for (let i = 0; i < xml_campos_primer_estado.length; i++ ) {
        let tabla_th = document.createElement("th");
        tabla_th.textContent = xml_campos_primer_estado[i].nodeName;
        tabla_tr.appendChild(tabla_th);
        campos.push(xml_campos_primer_estado[i].nodeName);
      }
      /* Cuerpo (<tbody>) de la tabla */
      let xml_nodos = documento_xml.firstChild.childNodes;
      let tabla_tbody = document.createElement("tbody");
      tabla.appendChild(tabla_tbody);
      let total = 0;
      for (let i = 1; i < xml_nodos.length; i++) {
        total += 1;
        /* Contador */
        let tabla_tr = document.createElement("tr");
        let tabla_contador = document.createElement("td");
        tabla_contador.textContent = i;
        tabla_tr.appendChild(tabla_contador);
        /* Campos por estado */
        let nodo_elemento = xml_nodos[i].childNodes;
        for (let j = 0; j < campos.length; j++) {
          let tabla_td = document.createElement("td");
          if (nodo_elemento[j] && campos[j] === nodo_elemento[j].nodeName) {
            tabla_td.textContent = nodo_elemento[j].textContent;
          } else {
            /* Si el estado no cuenta con este campo */
            tabla_td.textContent = "N/A";
          }
          tabla_tr.appendChild(tabla_td);
        }
        tabla_tbody.appendChild(tabla_tr);
      }
      let cantidad_de_codigos_postales = document.getElementById("cantidad_de_codigos_postales");
      cantidad_de_codigos_postales.textContent = total + " códigos postales";
    }
    let descargar = (evento) => {
      let archivo_a_descargar = document.getElementById("archivo_a_descargar");
      let mensaje = document.getElementById("mensaje");
      let cantidad_de_codigos_postales = document.getElementById("cantidad_de_codigos_postales");
      let tabla = document.getElementById("resultado");
      mensaje.textContent = "";
      cantidad_de_codigos_postales.textContent = "";
      /* Eliminación de thead y tbody de table */
      if (tabla.firstChild && tabla.lastChild) {
        tabla.removeChild(tabla.firstChild);
        tabla.removeChild(tabla.lastChild);
      }
      if (! /^\w+\.xml$/.test(archivo_a_descargar.value)) {
        mensaje.textContent = "Seleccione una entidad.";
        return false;
      }
      let servidor = "http://fetch02.com:8082";
      let url = servidor + "/xml/" + archivo_a_descargar.value + "?r=" + Math.random();
      let cabeceras_http = new Headers();
      let opciones_solicitud_http = {
        "method": "GET",
        "headers": cabeceras_http,
        "mode": "cors",
        "cache": "default"
      };
      let solicitud_http = new Request(url, opciones_solicitud_http);
      fetch(solicitud_http)
        .then((objeto_respuesta) => {
          let mensaje = document.getElementById("mensaje");
          let cantidad_de_codigos_postales = document.getElementById("cantidad_de_codigos_postales");
          mensaje.textContent = "";
          cantidad_de_codigos_postales.textContent = "";
         if (objeto_respuesta.status === 200) {
           objeto_respuesta.text().then((texto) => {
            /* Se requiere convertir el texto (string) a un documento de xml (XMLDocument) */
            let parseador_xml = new DOMParser();
            let documento_xml = parseador_xml.parseFromString(texto, "application/xml");
            actualizarTabla(documento_xml);
           });
         } else {
           mensaje.textContent = "Error " + objeto_respuesta.status + " " + objeto_respuesta.statusText + " - " + objeto_respuesta.url;
         }
        })
        .catch((objeto_error) => {
          mensaje.textContent = "Error " + objeto_error.name + " : " + objeto_error.message;
        });
    };
    let iniciar = () => {
      let boton_descargar = document.getElementById("descargar");
      boton_descargar.addEventListener('click', descargar, false);
    };
    
  3. Crea el directorio xml y deposita en él todos los archivos xml con los códigos postales obtenidos en la anterior práctica. Actualiza el archivo index.html de igual forma.

  4. Accede a http://fetch02.com:8082 y realiza lo mismo que en la práctica anterior.

Práctica 03

Sobre el envío de datos al servidor mediante fetch() y los métodos GET y POST de HTTP.

El uso de Python en el lado del servidor es solo para demostración de la recepción de los datos capturados en el formulario de HTML usando fetch(), pero además del como fetch() puede hacer algo con los datos recibidos del servidor. Es posible tener cualquier otro lenguaje de programación en el lado del servidor.

  1. Crea el archivo index.html con el siguiente contenido:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>fetch03</title>
        <style>
          body {
            width: 960px;
            margin: 10px auto 30px auto;
          }
          #archivo_a_descargar {
            width: 100%;
          }
          #respuesta {
            width: 100%;
            background-color: #f2f2f2;
          }
          #mensaje {
            color: red;
          }
        </style>
      </head>
      <body onload="App.main();">
        <h1>fetch03</h1>
        <form id="formulario">
          <fieldset>
            <legend>Formulario</legend>
            <p>
              <label for="nombre">Nombre:</label>
              <input type="text" id="nombre" />
            </p>
            <p>
              <label for="nacimiento">Año de nacimiento:</label>
              <input type="text" id="nacimiento" />
            </p>
            <p>
              <label for="metodo_http">Método HTTP:</label>
              <select id="metodo_http">
                <option value="GET">GET</option>
                <option value="POST">POST</option>
              </select>
            </p>
            <p>
              <input type="button" value="Enviar" id="descargar" />
           </p>
          </fieldset>
        </form>
        <h1>Resultado:</h1>
        <pre id="respuesta"></pre>
        <p id="mensaje"></p>
        <script src="js/fetch03a.js"></script>
      </body>
    </html>
    
  2. Crea el archivo js/fetch03a.js con el siguiente contenido:

    let App = (() => {
      let _descargar = (evento) => {
        let url = "http://fetch03.com:8083/cgi-bin";
    
        let parametros = "nombre=" +
          document.getElementById("nombre").value + "&" +
          "nacimiento=" +
          document.getElementById("nacimiento").value;
    
        if (document.getElementById("metodo_http").value === "GET") {
          let cabeceras_http = new Headers();
          let opciones_solicitud_http = {
            "method": "GET",
            "headers": cabeceras_http,
            "mode": "cors",
            "cache": "default"
          };
          let url_get = url + "/get.py" + "?r=" + Math.random() + "&" + parametros;
          let solicitud_http = new Request(url_get, opciones_solicitud_http);
          fetch(solicitud_http)
            .then((objeto_respuesta) => {
              let respuesta = document.getElementById("respuesta");
              let mensaje = document.getElementById("mensaje");
              respuesta.textContent = "";
              mensaje.textContent = "";
              if (objeto_respuesta.status === 200) {
                objeto_respuesta.text().then((texto) => {
                  respuesta.textContent = texto;
                });
              } else {
                mensaje.textContent = "Error " + objeto_respuesta.status + " " + objeto_respuesta.statusText + " - " + objeto_respuesta.url;
              }
            })
            .catch((objeto_error) => {
              mensaje.textContent = "Error " + objeto_error.name + " : " + objeto_error.message;
            });
        } else {
          let cabeceras_http = new Headers();
          cabeceras_http.append("Content-Type", "application/x-www-form-urlencoded");
          cabeceras_http.append("Content-Length", parametros.length);
          cabeceras_http.append("Connection", "close");
          let opciones_solicitud_http = {
            "method": "POST",
            "headers": cabeceras_http,
            "mode": "cors",
            "cache": "default",
            "body": parametros
          };
          let url_post = url + "/post.py" + "?r=" + Math.random();
          let solicitud_http = new Request(url_post, opciones_solicitud_http);
          fetch(solicitud_http)
            .then((objeto_respuesta) => {
              let respuesta = document.getElementById("respuesta");
              let mensaje = document.getElementById("mensaje");
              respuesta.textContent = "";
              mensaje.textContent = "";
              if (objeto_respuesta.status === 200) {
                objeto_respuesta.text().then((texto) => {
                respuesta.textContent = texto;
                });
              } else {
                mensaje.textContent = "Error " + objeto_respuesta.status + " " + objeto_respuesta.statusText + " - " + objeto_respuesta.url;
              }
            })
            .catch((objeto_error) => {
              mensaje.textContent = "Error " + objeto_error.name + " : " + objeto_error.message;
            });
        }
      };
      let _main = () => {
        let boton_descargar = document.getElementById("descargar");
        boton_descargar.addEventListener('click', _descargar, false);
      };
      return {
        "main": _main
      };
    })();
    
  3. Crea el archivo servidor.py con el siguiente contenido:

    #!/usr/bin/env python3
    import http.server
    puerto = 8083
    servidor = http.server.HTTPServer(("", puerto), http.server.CGIHTTPRequestHandler)
    try:
      print("Atendiendo solicitudes en ", puerto)
      servidor.serve_forever()
    except KeyboardInterrupt:
      print("Deteniendo servidor...")
      servidor.socket.close()
    
  4. Crea el directorio cgi-bin y en él los archivos get.py y post.py con el siguiente contenido en ambos casos:

    #!/usr/bin/env python3
    import os, cgi
    solicitud = cgi.FieldStorage()
    agente_de_usuario = os.environ["HTTP_USER_AGENT"]
    referencia_http = os.environ["HTTP_REFERER"]
    archivo_cgi = os.environ["SCRIPT_NAME"]
    nombre = ""
    nacimiento = ""
    if "nombre" in solicitud:
      nombre = solicitud["nombre"].value
    if "nacimiento" in solicitud:
      nacimiento = solicitud["nacimiento"].value
    print("Content-Type: text/plain")
    print("")
    print("Servidor:", referencia_http)
    print("Script:", archivo_cgi)
    print("Agente de usuario:", agente_de_usuario)
    print("")
    print("nombre =", nombre)
    print("nacimiento =", nacimiento)
    
  5. Asigna permisos de ejecución a los archivos cgi-bin/get.py y cgi-bin/post.py:

    alumno@servidor:~/www/fetch03 $ chmod +x cgi-bin/get.py cgi-bin/post.py
    

    Hasta este punto tendrás la siguiente estructura en $HOME/www/fetch03:

    alumno@servidor:~/www/fetch03 $ pwd && tree
    /home/alumno/www/fetch03
    .
    ├── cgi-bin
    │   ├── get.py
    │   └── post.py
    ├── index.html
    ├── js
    │   └── fetch03a.js
    └── servidor.py
    2 directories, 5 files
    
  6. Inicia el servidor web haciendo uso de Python:

    alumno@servidor:~/www/fetch03 $ python3 servidor.py
    
  7. Accede a http://fetch03.com:8083, rellena el formulario, selecciona alguna de las opciones de Método HTTP, da clic al botón Enviar y revisa el documento HTML obtenido desde el servidor.

Observaciones:

  • El formulario en index.html no define sus atributos method y action.
  • Podrías generar un documento XML de forma dinámica desde el lado del servidor y solicitar dicho documento desde el lado del cliente. En el lado del cliente (Navegador Web + JavaScript + DOM + fetch()) crear una Interfaz Gráfica de Usuario que interactue con el lado del servidor.