Comprar EPUB/PDF
ES
→ Solicitudes de red
21 de abril de 2022
Fetch
JavaScript puede enviar peticiones de red al servidor y cargar nueva información siempre que se necesite.
Por ejemplo, podemos utilizar una petición de red para:
● Crear una orden,
● Cargar información de usuario,
● Recibir las últimas actualizaciones desde un servidor,
● …etc.
…Y todo esto sin la necesidad de refrescar la página.
Se utiliza el término global “AJAX” (abreviado Asynchronous JavaScript And XML, en español: “JavaScript y XML
Asincrónico”) para referirse a las peticiones de red originadas desde JavaScript. Sin embargo, no estamos
necesariamente condicionados a utilizar XML dado que el término es antiguo y es por esto que el acrónimo XML
se encuentra aquí. Probablemente lo hayáis visto anteriormente.
Existen múltiples maneras de enviar peticiones de red y obtener información de un servidor.
Comenzaremos con el el método fetch() que es moderno y versátil. Este método no es soportado por
navegadores antiguos (sin embargo se puede incluir un polyfill), pero es perfectamente soportado por los
navegadores actuales y modernos.
La sintaxis básica es la siguiente:
1 let promise = fetch(url, [options])
● url – representa la dirección URL a la que deseamos acceder.
● options – representa los parámetros opcionales, como puede ser un método o los encabezados de nuestra
petición, etc.
Si no especificamos ningún options , se ejecutará una simple petición GET, la cual descargará el contenido de
lo especificado en el url .
El navegador lanzará la petición de inmediato y devolverá una promesa (promise) que luego será utilizada por el
código invocado para obtener el resultado.
Por lo general, obtener una respuesta es un proceso de dos pasos.
Primero, la promesa promise , devuelta por fetch , resuelve la respuesta con un objeto de la clase
incorporada Response tan pronto como el servidor responde con los encabezados de la petición.
En este paso, podemos chequear el status HTTP para poder ver si nuestra petición ha sido exitosa o no, y
chequear los encabezados, pero aún no disponemos del cuerpo de la misma.
La promesa es rechazada si el fetch no ha podido establecer la petición HTTP, por ejemplo, por problemas de
red o si el sitio especificado en la petición no existe. Estados HTTP anormales, como el 404 o 500 no generan
errores.
Podemos visualizar los estados HTTP en las propiedades de la respuesta:
● status – código de estado HTTP, por ejemplo: 200.
● ok – booleana, true si el código de estado HTTP es 200 a 299.
Ejemplo:
1 let response = await fetch(url);
2
3 if ([Link]) { // si el HTTP-status es 200-299
4 // obtener cuerpo de la respuesta (método debajo)
5 let json = await [Link]();
6 } else {
7 alert("Error-HTTP: " + [Link]);
8 }
Segundo, para obtener el cuerpo de la respuesta, necesitamos utilizar un método adicional.
Response provee múltiples métodos basados en promesas para acceder al cuerpo de la respuesta en distintos
formatos:
● [Link]() – lee y devuelve la respuesta en formato texto,
● [Link]() – convierte la respuesta como un JSON,
● [Link]() – devuelve la respuesta como un objeto FormData (explicado en el siguiente
capítulo),
● [Link]() – devuelve la respuesta como Blob (datos binarios tipados),
● [Link]() – devuelve la respuesta como un objeto ArrayBuffer (representación binaria de
datos de bajo nivel),
● Adicionalmente, [Link] es un objeto ReadableStream, el cual nos permite acceder al cuerpo como
si fuera un stream y leerlo por partes. Veremos un ejemplo de esto más adelante.
Por ejemplo, si obtenemos un objeto de tipo JSON con los últimos commits de GitHub:
1 let url = '[Link]
2 let response = await fetch(url);
3
4 let commits = await [Link](); // leer respuesta del cuerpo y devolver co
5
6 alert(commits[0].[Link]);
O también usando promesas, en lugar de await :
1 fetch('[Link]
2 .then(response => [Link]())
3 .then(commits => alert(commits[0].[Link]));
Para obtener la respuesta como texto, await [Link]() en lugar de .json() :
1 let response = await fetch('[Link]
2
3 let text = await [Link](); // leer cuerpo de la respuesta como texto
4
5 alert([Link](0, 80) + '...');
Como demostración de una lectura en formato binario, hagamos un fetch y mostremos una imagen del logotipo
de “especificación fetch” (ver capítulo Blob para más detalles acerca de las operaciones con Blob ):
1 let response = await fetch('/article/fetch/[Link]');
2
3 let blob = await [Link](); // download as Blob object
4
5 // crear tag <img> para imagen
6 let img = [Link]('img');
7 [Link] = 'position:fixed;top:10px;left:10px;width:100px';
8 [Link](img);
9
10 // mostrar
11 [Link] = [Link](blob);
12
13 setTimeout(() => { // ocultar luego de tres segundos
14 [Link]();
15 [Link]([Link]);
16 }, 3000);
Importante:
Podemos elegir un solo método de lectura para el cuerpo de la respuesta.
Si ya obtuvimos la respuesta con [Link]() , entonces [Link]() no funcionará, dado
que el contenido del cuerpo ya ha sido procesado.
1 let text = await [Link](); // cuerpo de respuesta obtenido y procesa
2 let parsed = await [Link](); // fallo (ya fue procesado)
Encabezados de respuesta
Los encabezados de respuesta están disponibles como un objeto de tipo Map dentro del [Link] .
No es exactamente un Map, pero posee métodos similares para obtener de manera individual encabezados por
nombre o si quisiéramos recorrerlos como un objeto:
1 let response = await fetch('[Link]
2
3 // obtenemos un encabezado
4 alert([Link]('Content-Type')); // application/json; charset=utf-8
5
6 // iteramos todos los encabezados
7 for (let [key, value] of [Link]) {
8 alert(`${key} = ${value}`);
9 }
Encabezados de petición
Para especificar un encabezado en nuestro fetch , podemos utilizar la opción headers . La misma posee un
objeto con los encabezados salientes, como se muestra en el siguiente ejemplo:
1 let response = fetch(protectedUrl, {
2 headers: {
3 Authentication: 'secret'
4 }
5 });
…Pero existe una lista de encabezados que no pueden ser especificados:
● Accept-Charset , Accept-Encoding
● Access-Control-Request-Headers
● Access-Control-Request-Method
● Connection
● Content-Length
● Cookie , Cookie2
● Date
● DNT
● Expect
● Host
● Keep-Alive
● Origin
● Referer
● TE
● Trailer
● Transfer-Encoding
● Upgrade
● Via
● Proxy-*
● Sec-*
Estos encabezados nos aseguran que nuestras peticiones HTTP sean controladas exclusivamente por el
navegador, de manera correcta y segura.
Peticiones POST
Para ejecutar una petición POST , o cualquier otro método, utilizaremos las opciones de fetch :
● method – método HTTP, por ej: POST ,
● body – cuerpo de la respuesta, cualquiera de las siguientes:
● cadena de texto (ej. JSON-encoded),
● Objeto FormData , para enviar información como multipart/form-data ,
● Blob / BufferSource para enviar información en formato binario,
● URLSearchParams, para enviar información en cifrado x-www-form-urlencoded (no utilizado
frecuentemente).
El formato JSON es el más utilizado.
Por ejemplo, el código debajo envía la información user como un objeto JSON:
1 let user = {
2 nombre: 'Juan',
3 apellido: 'Perez'
4 };
5
6
7
8
9
10 let response = await fetch('/article/fetch/post/user', {
11 method: 'POST',
12 headers: {
13 'Content-Type': 'application/json;charset=utf-8'
14 },
15 body: [Link](user)
});
let result = await [Link]();
alert([Link]);
Tener en cuenta, si la respuesta del body es una cadena de texto, entonces el encabezado Content-Type será
especificado como text/plain;charset=UTF-8 por defecto.
Pero, cómo vamos a enviar un objeto JSON, en su lugar utilizaremos la opción headers especificada a
application/json , que es la opción correcta Content-Type para información en formato JSON.
Enviando una imagen
También es posible enviar datos binarios con fetch , utilizando los objetos Blob o BufferSource .
En el siguiente ejemplo, utilizaremos un <canvas> donde podremos dibujar utilizando nuestro ratón. Haciendo
click en el botón “enviar” enviará la imagen al servidor:
1 <body style="margin:0">
2 <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></ca
3
4 <input type="button" value="Enviar" onclick="submit()">
5
6 <script>
7 [Link] = function(e) {
8 let ctx = [Link]('2d');
9 [Link]([Link], [Link]);
10 [Link]();
11 };
12
13 async function submit() {
14 let blob = await new Promise(resolve => [Link](resolve, 'image
15 let response = await fetch('/article/fetch/post/image', {
16 method: 'POST',
17 body: blob
18 });
19
20 // el servidor responde con una confirmación y el tamaño de nuestra image
21 let result = await [Link]();
22 alert([Link]);
23 }
24
25 </script>
26 </body>
Enviar
Una aclaración, aquí no especificamos el Content-Type de manera manual, precisamente porque el objeto
Blob posee un tipo incorporado (en este caso image/png , el cual es generado por la función toBlob ). Para
objetos Blob ese es el valor por defecto del encabezado Content-Type .
Podemos reescribir la función submit() sin utilizar async/await de la siguiente manera:
1 function submit() {
2 [Link](function(blob) {
3 fetch('/article/fetch/post/image', {
4 method: 'POST',
5 body: blob
6 })
7 .then(response => [Link]())
8 .then(result => alert([Link](result, null, 2)))
9 }, 'image/png');
10 }
Resumen
Una petición fetch típica está formada por dos llamadas await :
1 let response = await fetch(url, options); // resuelve con los encabezados de re
2 let result = await [Link](); // accede al cuerpo de respuesta como json
También se puede acceder sin utilizar await :
1 fetch(url, options)
2 .then(response => [Link]())
3 .then(result => /* procesa resultado */)
Propiedades de respuesta:
● [Link] – Código HTTP de la respuesta.
● [Link] – Devuelve true si el código HTTP es 200-299.
● [Link] – Objeto simil-Map que contiene los encabezados HTTP.
Métodos para obtener el cuerpo de la respuesta:
● [Link]() – lee y devuelve la respuesta en formato texto,
● [Link]() – convierte la respuesta como un JSON,
● [Link]() – devuelve la respuesta como un objeto FormData (codificación
multipart/form-data , explicado en el siguiente capítulo),
● [Link]() – devuelve la respuesta como Blob (datos binarios tipados),
● [Link]() – devuelve la respuesta como un objeto ArrayBuffer (datos binarios de bajo
nivel)
Opciones de fetch hasta el momento:
● method – método HTTP,
● headers – un objeto los encabezados de la petición (no todos los encabezados están permitidos),
● body – los datos/información a enviar (cuerpo de la petición) como string , FormData , BufferSource ,
Blob u objeto UrlSearchParams .
En los próximos capítulos veremos más sobre opciones y casos de uso para fetch .
Tareas
Fetch de usuarios de GitHub
Crear una función async llamada getUsers(names) , que tome como parámetro un arreglo de logins de GitHub,
obtenga el listado de usuarios de GitHub indicado y devuelva un arreglo de usuarios de GitHub.
La url de GitHub con la información de usuario especifica USERNAME es:
[Link] .
En el ambiente de prueba (sandbox) hay un ejemplo de referencia.
Detalles a tener en cuenta:
1. Debe realizarse una única petición fetch por cada usuario.
2. Para que la información esté disponible lo antes posible las peticiones no deben ejecutarse de una por vez.
3. Si alguna de las peticiones fallara o si el usuario no existiese, la función debe devolver null en el
resultado del arreglo.
Abrir en entorno controlado con pruebas.
solución
Lección anterior Próxima lección
Compartir Mapa del Tutorial
Comentarios
● Si tiene sugerencias sobre qué mejorar, por favor enviar una propuesta de GitHub o una solicitud de
extracción en lugar de comentar.
● Si no puede entender algo en el artículo, por favor explique.
● Para insertar algunas palabras de código, use la etiqueta <code> , para varias líneas – envolverlas en
la etiqueta <pre> , para más de 10 líneas – utilice una entorno controlado (sandbox) (plnkr, jsbin,
codepen…)
© 2007—2024 Ilya Kantoracerca del proyecto
contáctenos