INTRODUCCIÓN
El HTTP son las siglas de «Hypertext Transfer Protocol«, el protocolo de comunicación que permite las transferencias de información en la WEB (World Wide Web).
El HTTP funciona como un protocolo de solicitud-respuesta (request-response) entre un cliente y un servidor.
Un cliente puede ser un navegador WEB (Microsoft Edge, Safari, Mozzilla, Google Chrome, …) ejecutado desde un ordenador y un servidor puede ser un chip ESP8266 que aloja una o varias páginas WEB. Cuando el cliente envía una solicitud HTTP al servidor, el servidor envía la respuesta al cliente.
Los dos métodos más utilizados para una solicitud-respuesta entre un cliente y un servidor son GET y POST, conceptualmente tienen los siguientes usos:
- GET: obtener información del servidor y enviársela al cliente (una página WEB almacenada, un archivo, datos de bases de datos, …). Puede ser necesario enviar información en la solicitud al servidor , para que pueda devolver la respuesta al cliente.
- POST: enviar información desde el cliente al servidor para que la procese, agregue o actualice.
MATERIAL NECESARIO
CONEXIONADO
COMUNICACIONES HTTP
Para cada uno de los ejemplos, serán necesarias dos comunicaciones HTTP para lograr el encendido/apagado de los LEDs. La primera servirá para solicitar y obtener el formulario HTML de control y la segunda para enviar el formulario cumplimentado, con las ordenes del nuevo estado de los LEDs al chip ESP8266 y obtener la respuesta del servidor. A continuación se van a desarrollar en detalle:
PRIMERA COMUNICACIÓN:
El cliente solicita al servidor, utilizando el método GET, el formulario de control de encendido/apagado y el servidor lo envía como respuesta. En la solicitud no es preciso enviar ningún dato al servidor -lo que justifica el método utilizado, que será el mismo en los dos ejemplos-.
Es importante que se entienda que cuando escribimos una URL en un navegador (como www.esploradores.com), estamos realizando una solicitud HTTP de un recurso de un servidor (página WEB, foto, vídeo…), utilizando el método GET.
Para llevar a cabo la comunicación, únicamente será necesario escribir la ruta (path) en la que se aloja el formulario dentro del servidor, en la barra del navegador WEB del cliente.
SEGUNDA COMUNICACIÓN:
En un ejemplo los datos de encendido/apagado del formulario se enviarán al servidor utilizando el método GET y en el otro el método POST.
El cliente solicita al servidor el encendido/apagado de los LEDs. La solicitud se realizará mediante un formulario en el que se podrán seleccionar las opciones de encendido/apagado para cada uno de los LEDs.
Para llevar a cabo las comunicaciones se dará ENVIAR en el formulario. El cliente recibirá como respuesta del servidor el nuevo estado de los LEDs.
CODIGO WEB (HTML)
Comenzaremos escribiendo las páginas WEB con el formulario, -una por cada método-, para poder compararlas.
Su estructura va a ser muy sencilla. Tendrá las dos opciones seleccionables (ENCENDIDO/APAGADO), para los estados de los dos LEDs. Al final se colocará un botón de ENVIAR, para realizar la solicitud al servidor.
MÉTODO GET:
<!DOCTYPE html> <!--Declaración del tipo de documento: HTML5--> <html> <!--Inicio del documento HTML--> <head> <!--Inicio de la cabecera -inf.sobre el doc.--> <meta charset=utf-8> <!--Config. de carácteres utilizada: UTF-8--> <title>GET&POST</title> <!--Título del documento --> </head> <!--Fin de la cabecera --> <body> <!--Inicio el cuerpo -contenido visible del doc.--> <h2>COMUNICACIONES HTTP</h2> <!--Texto de encabezado -título (h2)--> <form action='/led' method='get'> <!--Inicio del formulario. La URL de destino es "/led" = URL en la que se visualiza el formulario + led (-en el ejemplo- http://192.168.1.135/led). El método de envío es "GET". Si no se selecciona un método, por defecto será "GET".--> <input type='radio' name='led1' value='1' checked> ENCENDER LED 1   <!--Primer valor seleccionable para led1: led1=1 --> <input type='radio' name='led1' value='0'>APAGAR LED 1<br> <!--Segundo valor seleccionable para led1: led1=0 --> <input type='radio' name='led2' value='1' checked> ENCENDER LED 2   <!--Primer valor seleccionable para led2: led2=1 --> <input type='radio' name='led2' value='0'>APAGAR LED 2<br><br> <!--Segundo valor seleccionable para led2: led2=0 --> <input type='submit' value='ENVIAR'> <!--Envío de los dos pares nombre/valor seleccionados--> </form> <!--Fin del formulario--> </body> <!--Fin del cuerpo--> </html> <!--Fin del documento HTML-->
MÉTODO POST:
<!DOCTYPE html> <!--Declaración del tipo de documento: HTML5--> <html> <!--Inicio del documento HTML--> <head> <!--Inicio de la cabecera -inf.sobre el doc.--> <meta charset=utf-8> <!--Config. de carácteres utilizada: UTF-8--> <title>GET&POST</title> <!--Título del documento --> </head> <!--Fin de la cabecera --> <body> <!--Inicio el cuerpo -contenido visible del doc.--> <h2>COMUNICACIONES HTTP</h2> <!--Texto de encabezado -título (h2)--> <form action='/led' method='post'> <!--Inicio del formulario. La URL de destino es "/led" = URL en la que se visualiza el formulario + led (-en el ejemplo- http://192.168.1.135/led). El método de envío es "POST".--> <input type='radio' name='led1' value='1' checked> ENCENDER LED 1   <!--Primer valor seleccionable para led1: led1=1 --> <input type='radio' name='led1' value='0'>APAGAR LED 1<br> <!--Segundo valor seleccionable para led1: led1=0 --> <input type='radio' name='led2' value='1' checked> ENCENDER LED 2   <!--Primer valor seleccionable para led2: led2=1 --> <input type='radio' name='led2' value='0'>APAGAR LED 2<br><br> <!--Segundo valor seleccionable para led2: led2=0 --> <input type='submit' value='ENVIAR'> <!--Envío de los dos pares nombre/valor seleccionados--> </form> <!--Fin del formulario--> </body> <!--Fin del cuerpo--> </html> <!--Fin del documento HTML-->
Como se puede ver, los formularios que están contenidos entre las líneas 9 y 17 de cada código HTML y son idénticos, lo único que varía es el método («method«), get o post que se utiliza para enviar los pares que forman los nombres «led1» y «led2» y sus valores seleccionables («1» para encendido y «0» para apagado).
ESA ES LA ÚNICA DIFERENCIA QUE EXISTE ENTRE LA ELECCIÓN DE UNA COMUNICACIÓN GET O POST, YA QUE AMBAS SE GESTIONAN IGUAL, INDEPENDIENTEMENTE DE LA FORMA EN QUE SE ENVÍEN LOS DATOS.
Las páginas son estáticas (no cambian nunca) y en el sketch –como se verá más adelante- se construyen mediante una cadena de texto, que se envía como respuesta a la petición del servidor dirigida a la ruta en la que se almacena («/»).
Al ENVIAR al formulario la nueva solicitud HTTP se dirige a la ruta «/led«. En el sketch, en esta ruta, se establece la forma en que el chip ESP8266 gestionará el encendido/apagado de los LEDs y el envío de la página WEB de respuesta al cliente. La página WEB en este caso será dinámica, es decir, la construye el servidor condicionada al la forma en que ha recibido la comunicación HTTP de petición (línea 82) y el estado de los LEDs (líneas 84 y 85 del sketch):
String estado = "<!DOCTYPE html>"; //Declaración del tipo de documento: HTML5 estado += "<html>"; //Inicio del documento HTML estado += "<head>"; //Inicio de la cabecera -inf.sobre el doc.- estado += "<meta charset=utf-8>"; //Config. de carácteres utilizada: UTF-8 estado += "<title>GET&POST</title>"; //Título del documento estado += "</head>"; //Fin de la cabecera estado += "<body>"; //Inicio del cuerpo -contenido visible del doc.- estado += server.method() == HTTP_GET?"<h2>METODO GET</h2>":"<h2>METODO POST</h2>"; // Texto de encabezado -h2-. Varía en función del método utilizado estado += "<h3>RESPUESTA DEL SERVIDOR</h3>"; // Texto de encabezado -h3-. estado += digitalRead(LED1)?"<p>El LED 1 está encendido<br>":"<p>El LED 1 está apagado<br>"; // Estado del LED1. Varía en función de la lectura de su estado. estado += digitalRead(LED2)?"El LED 2 está encendido</p>":"El LED 2 está apagado</p>"; // Estado del LED2. Varía en función de su lectura de su estado. estado += "</body>"; //Fin del cuerpo estado += "</html>"; //Fin del documento HTML
Los anteriores páginas WEB en un navegador se visualizarán así:
Ambas son idénticas, lo único que varía es como se envían los datos recabados por el formulario al servidor.
Podemos verlo utilizando algún plugin que nos permita interceptar las comunicaciones entre cliente y servidor. En este caso utilizando WebSpy (un complemento para desarrolladores de Google Chrome), obtenemos las siguientes capturas de pantalla cuando damos «ENVIAR«:
Observamos que:
Ese es el motivo por el que:
Las solicitudes GET… … permanecen en el historial del navegador. … pueden almacenarse en la caché WEB. … tienen restricciones de longitud. … solo se deben utilizar para solicitar información (no modificarla). … NUNCA se deben usar cuando se trata de datos confidenciales. |
Las solicitudes POST… … NO permanecen en el historial del navegador. … NO pueden almacenarse en la caché WEB. … NO tienen restricciones de longitud. … sirven para enviar información al servidor (para que se procese, agregue o actualice). |
SKETCH
El sketch de Arduino a subir al chip ESP8266 es el que se muestra a continuación:
/* Ejemplo de comunicaciones HTTP tipo GET & POST. Escrito por Dani No www.esploradores.com Este sofware está escrito bajo la licencia CREATIVE COMMONS con Reconocimiento-CompartirIgual(CC BY-SA) https://creativecommons.org/ -Redistributions of source code must retain the above creative commons and this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above creative commons notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHTHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <ESP8266WiFi.h> //Incluye una librería externa para gestionar la conexión WiFi #include <ESP8266WebServer.h> //Incluye una librería externa para facilitar la gestión del servidor #define LED1 02 // D4 Asignación del nombre -LED1- al pin para el encendido/apagado de un led #define LED2 05 // D1 Asignación del nombre -LED2- al pin para el encendido/apagado de un led const char* ssid = "___________"; //Nombre de la red WiFi para la conexión (modo station) const char* password = "___________"; //Clave de la red WiFi ESP8266WebServer server(80); //Declara el puerto 80 del router para uso para las comunciaciones HTTP con el servidor void setup() { //Declara la func. SETUP -Configuración inicial del sketch- Serial.begin(115200); //Velocidad del Puerto Serie en baudios (115200) Serial.println(); //Salto de línea en el Puerto Serie pinMode(LED1, OUTPUT); //Inicializa el pin LED1 como salida pinMode(LED2, OUTPUT); //Inicializa el pin LED2 como salida WiFi.begin(ssid, password); //Inicializa la conexión a la red WiFi while (WiFi.status() != WL_CONNECTED) { //Espera hasta que se logra la conexión delay(500); //Cada 0,5s hasta que se conecta... Serial.print("."); //... imprime "." en el puerto serie } Serial.printf("\nConectado a la red: %s", WiFi.SSID().c_str()); //Imprime en el pto. serie el nombre de la red WiFi de conexión Serial.printf("\nDirección IP: http://%s\n", WiFi.localIP().toString().c_str()); //Imprime en el pto. serie la dirección IP asignada por el router //CONFIGURACIÓN DE LAS RUTAS ("PATHS") DEL SERVIDOR HTTP server.on("/", [](){ //RUTA "/" DE SOLICITUD HTTP String formulario = //Construcción del formulario HTML de respuesta en cadena de texto "<!DOCTYPE html>" //Declaración del tipo de documento: HTML5 "<html>" //Inicio del documento HTML "<head>" //Inicio de la cabecera -inf.sobre el doc.- "<meta charset=utf-8>" //Config. de carácteres utilizada: UTF-8 "<title>GET&POST</title>" //Título del documento "</head>" //Fin de la cabecera "<body>" //Inicio del cuerpo -contenido visible del doc.- "<h2>COMUNICACIONES HTTP</h2>" //Texto de encabezado -h2- "<form action='led' method='get'>" //Inicio del formulario. Método de envío GET // "<form action='led' method='post'>" //Inicio del formulario. Método de envío POST "<input type='radio' name='led1' value='1' checked> ENCENDER LED 1 " //Primer valor seleccionable para led1: led1=1 "<input type='radio' name='led1' value='0'>APAGAR LED 1<br>" //Segundo valor seleccionable para led1: led1=0 "<input type='radio' name='led2' value='1' checked> ENCENDER LED 2 " //Primer valor seleccionable para led2: led2=1 "<input type='radio' name='led2' value='0'>APAGAR LED 2<br><br>" //Segundo valor seleccionable para led2: led2=0 "<input type='submit' value='ENVIAR'>" //Envío de los dos pares nombre/valor seleccionados "</form>" //Fin del formulario "</body>" //Fin del cuerpo "</html>"; //Fin del documento HTML server.send(200, "text/html", formulario); //Envío del formulario como respuesta al cliente }); //MIME text/html: documento de texto en formato HTML //https://developer.mozilla.org/es/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Lista_completa_de_tipos_MIME server.on("/led", [](){ //RUTA "/led" DE SOLICITUD HTTP int estado_led1 = server.arg("led1").toInt(); //Obtiene el valor enviado de "led1" como STRING y lo convierte a INT int estado_led2 = server.arg("led2").toInt(); //Obtiene el valor enviado de "led2" como STRING y lo convierte a INT digitalWrite(LED1, estado_led1); //Cambia el estado del pin LED1 según la selección del formulario digitalWrite(LED2, estado_led2); //Cambia el estado del pin LED2 según la selección del formulario //Construcción del formulario HTML de respuesta en cadena de texto //Va a ser un formulario dinámico (se establecen condiciones que varían el resultado final) String estado = "<!DOCTYPE html>"; //Declaración del tipo de documento: HTML5 estado += "<html>"; //Inicio del documento HTML estado += "<head>"; //Inicio de la cabecera -inf.sobre el doc.- estado += "<meta charset=utf-8>"; //Config. de carácteres utilizada: UTF-8 estado += "<title>GET&POST</title>"; //Título del documento estado += "</head>"; //Fin de la cabecera estado += "<body>"; //Inicio del cuerpo -contenido visible del doc.- estado += server.method() == HTTP_GET?"<h2>METODO GET</h2>":"<h2>METODO POST</h2>"; // Texto de encabezado -h2-. Varía en función del método utilizado estado += "<h3>RESPUESTA DEL SERVIDOR</h3>"; // Texto de encabezado -h3-. estado += digitalRead(LED1)?"<p>El LED 1 está encendido<br>":"<p>El LED 1 está apagado<br>"; // Estado del LED1. Varía en función de la lectura de su estado. estado += digitalRead(LED2)?"El LED 2 está encendido</p>":"El LED 2 está apagado</p>"; // Estado del LED2. Varía en función de su lectura de su estado. estado += "</body>"; //Fin del cuerpo estado += "</html>"; //Fin del documento HTML server.send(200, "text/html", estado); //Envío del formulario como respuesta al cliente }); server.begin(); //Inicializa el servidor Serial.println("Servidor HTTP inicializado"); //Imprime en el pto. serie que el servidor se ha inicializado } void loop() { //Declara la func. LOOP -Funciones del sketch que se repiten indefinidamente- server.handleClient(); //El servidor comprueba las conexiones entrantes de clientes }
Una vez que hayamos subido el sketch en el chip ESP8266, podremos ver la dirección asignada por el router a través del puerto serie del IDE de Arduino. Esa será la URL que hay que escribir desde cualquier navegador WEB (Mozzilla, Google Chrome, Safari, Internet Explorer, Microsoft Edge…) de un cliente conectado al mismo router, para iniciar la comunicación HTTP que solicitará el formulario al servidor.
MEJORA DEL SKETCH
De manera intencionada, para que se pueda apreciar claramente los pasos de solicitud/respuesta de las comunicaciones HTTP, se ha hecho que con el anterior sketch, una vez que se haya enviado la solicitud de encendido/apagado de los LEDs, a través del formulario y recibido la respuesta, se hayan finalizado las posibilidades de interactuar con el servidor, salvo que volvamos a cargar un nuevo el formulario desde el navegador. Es como tener una escopeta de un tiro.
Lo lógico es que cuando respondamos el formulario y enviemos la solicitud al servidor este nos devuelva un nuevo formulario. Esto se corrige en el siguiente sketch:
/* Ejemplo de comunicaciones HTTP tipo GET & POST. Escrito por Dani No www.esploradores.com Este sofware está escrito bajo la licencia CREATIVE COMMONS con Reconocimiento-CompartirIgual(CC BY-SA) https://creativecommons.org/ -Redistributions of source code must retain the above creative commons and this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above creative commons notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHTHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <ESP8266WiFi.h> //Incluye una librería externa para gestionar la conexión WiFi #include <ESP8266WebServer.h> //Incluye una librería externa para facilitar la gestión del servidor #define LED1 02 // D4 Asignación del nombre -LED1- al pin para el encendido/apagado de un led #define LED2 05 // D1 Asignación del nombre -LED2- al pin para el encendido/apagado de un led const char* ssid = "___________"; //Nombre de la red WiFi para la conexión (modo station) const char* password = "___________"; //Clave de la red WiFi ESP8266WebServer server(80); //Declara el puerto 80 del router para uso para las comunciaciones HTTP con el servidor void setup() { //Declara la func. SETUP -Configuración inicial del sketch- Serial.begin(115200); //Velocidad del Puerto Serie en baudios (115200) Serial.println(); //Salto de línea en el Puerto Serie pinMode(LED1, OUTPUT); //Inicializa el pin LED1 como salida pinMode(LED2, OUTPUT); //Inicializa el pin LED2 como salida WiFi.begin(ssid, password); //Inicializa la conexión a la red WiFi while (WiFi.status() != WL_CONNECTED) { //Espera hasta que se logra la conexión delay(500); //Cada 0,5s hasta que se conecta... Serial.print("."); //... imprime "." en el puerto serie } Serial.printf("\nConectado a la red: %s", WiFi.SSID().c_str()); //Imprime en el pto. serie el nombre de la red WiFi de conexión Serial.printf("\nDirección IP: http://%s\n", WiFi.localIP().toString().c_str()); //Imprime en el pto. serie la dirección IP asignada por el router //CONFIGURACIÓN DE LAS RUTAS ("PATHS") DEL SERVIDOR HTTP server.on("/", [](){ //RUTA "/" DE SOLICITUD HTTP formulario(); //Llamada a la función para enviar el formulario al cliente }); server.on("/led", [](){ //RUTA "/led" DE SOLICITUD HTTP int estado_led1 = server.arg("led1").toInt(); //Obtiene el valor enviado de "led1" como STRING y lo convierte a INT int estado_led2 = server.arg("led2").toInt(); //Obtiene el valor enviado de "led2" como STRING y lo convierte a INT digitalWrite(LED1, estado_led1); //Cambia el estado del pin LED1 según la selección del formulario digitalWrite(LED2, estado_led2); //Cambia el estado del pin LED2 según la selección del formulario formulario(); //Llamada a la función para enviar el formulario al cliente }); server.begin(); //Inicializa el servidor (una vez configuradas las rutas) Serial.println("Servidor HTTP inicializado"); //Imprime en el pto. serie que el servidor se ha inicializado } void loop() { //Declara la func. LOOP -Funciones del sketch que se repiten indefinidamente- server.handleClient(); //El servidor comprueba las conexiones entrantes de clientes } //Declara la función FORMULARIO void formulario(){ //Va a ser un formulario dinámico (se establecen condiciones que varían el resultado final) String form = "<!DOCTYPE html>"; //Declaración del tipo de documento: HTML5 form += "<html>"; //Inicio del documento HTML form += "<head>"; //Inicio de la cabecera -inf.sobre el doc.- form += "<meta charset=utf-8>"; //Config. de carácteres utilizada: UTF-8 form += "<title>METODOS GET&POST</title>"; //Título del documento form += "</head>"; //Fin de la cabecera form += "<body>"; //Inicio del cuerpo -contenido visible del doc.- form += "<form action='led' method='get'>"; //Inicio del formulario. Método de envío GET //form += "<form action='led' method='post'>"; //Inicio del formulario. Método de envío POST //Pares seleccionables (nombre/valor): led1/1 ó led1/0 y led2/1 y led2/0. //En el formulario se marca como pre-seleccionado el estado de los LEDs, obtenido a través de la lectura del pin. form += digitalRead(LED1)? "<input type='radio' name='led1' value='1' checked> ENCENDER LED 1 <input type='radio' name='led1' value='0'>APAGAR LED 1<br>": "<input type='radio' name='led1' value='1'> ENCENDER LED 1 <input type='radio' name='led1' value='0' checked>APAGAR LED 1<br>"; form += digitalRead(LED2)? "<input type='radio' name='led2' value='1' checked> ENCENDER LED 2 <input type='radio' name='led2' value='0'>APAGAR LED 2<br><br>": "<input type='radio' name='led2' value='1'> ENCENDER LED 2 <input type='radio' name='led2' value='0' checked>APAGAR LED 2<br><br>"; form += "<input type='submit' value='ENVIAR'>"; //Envío de los dos pares nombre/valor seleccionados form += "</form>"; //Fin del formulario form += "</body>"; //Fin del cuerpo form += "</html>"; //Fin del documento HTML server.send(200, "text/html", form); //Envío del formulario como respuesta al cliente }
12 Comments
Leave your reply.