10. Servicio que valida datos

Versión para imprimir.

A. Introducción

B. Diagrama de despliegue

Diagrama de despliegue

C. Escenario con datos incorrectos

Versión para imprimir.

1. El usuario activa la forma sin capturar datos

Forma

2. Se activa el código del evento submit

Forma

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

3. Se invoca el servicio, incluyendo los datos de la forma

Forma

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Request

POST /srv/valida.php HTTP/1.1 Accept: application/json, application/problem+json
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 230
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Cookie: __gsas=ID=329641bd2728ff51:T=1743534294:RT=1743534294:S=ALNI_MavjA3FPY-hDE5wNZO5LxFSOVeIIQ; __test=6409d42a9d124b0fcecbb489e113453c
Host: srvvalida.rf.gd
Origin: http://srvvalida.rf.gd
Referer: http://srvvalida.rf.gd/?i=1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

saludo:
nombre:
------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Content-Disposition: form-data; name="saludo"


------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Content-Disposition: form-data; name="nombre"


------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN--

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Despierta y recibe request.

4. El servicio lee los datos

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Request

POST /srv/valida.php HTTP/1.1 Accept: application/json, application/problem+json
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 230
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Cookie: __gsas=ID=329641bd2728ff51:T=1743534294:RT=1743534294:S=ALNI_MavjA3FPY-hDE5wNZO5LxFSOVeIIQ; __test=6409d42a9d124b0fcecbb489e113453c
Host: srvvalida.rf.gd
Origin: http://srvvalida.rf.gd
Referer: http://srvvalida.rf.gd/?i=1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

saludo:
nombre:
------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Content-Disposition: form-data; name="saludo"


------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN
Content-Disposition: form-data; name="nombre"


------WebKitFormBoundaryWv6BXE9Yr1Q8AqnN--

Memoria (Servidor)

$saludo
""
$nombre
""

5. El servicio comprueba que el saludo sea válido

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Memoria (Servidor)

$saludo
""
$nombre
""

6. Como el saludo no es válido, aborta y genera la response

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/errors/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Response

HTTP/1.1 400 Bad Request Server: openresty
Date: Fri, 02 May 2025 13:53:21 GMT
Content-Type: application/problem+json
Transfer-Encoding: chunked
Connection: keep-alive
{"title":"Falta el saludo.","type":"\/errors\/faltasaludo.html"}

7. El servicio devuelve la response, que es recibida en el cliente

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Despierta y recibe response.

Response

HTTP/1.1 400 Bad Request Server: openresty
Date: Fri, 02 May 2025 13:53:21 GMT
Content-Type: application/problem+json
Transfer-Encoding: chunked
Connection: keep-alive
{"title":"Falta el saludo.","type":"\/errors\/faltasaludo.html"}

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Devuelve response y se duerme.

8. Como hay error, lanza una excepción cuyos detalles se muestran en la consola y en un alert

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Memoria

error
ProblemDetails
status
400
type
"/errors/faltasaludo.html"
title
"Falta el saludo."

Alert

Falta el saludo

Consola

POST http://srvvalida.rf.gd/php/valida.php 400 (Bad Request) enviaFormRecibeJson.js:10 enviaFormRecibeJson @ enviaFormRecibeJson.js:10
submit @ ?i=1:56
ProblemDetailsError: Falta el saludo. muestraError.js:29
at consume (consume.js:16:10)
at async HTMLFormElement.submit (?i=1:55:22)
{status: 400, title: 'Falta el saludo.', type: '/erros/faltasaludo.html'} "status": 400
"title": "Falta el saludo."
"type": "/erros/faltasaludo.html"
[[Orototype]]: Object
muestraError @ muestraError.js:29
(anónimo) @ manejaErrores.js:32

9. Al cerrar el alert, termina el evento

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Consola

POST http://srvvalida.rf.gd/php/valida.php 400 (Bad Request) enviaFormRecibeJson.js:10 enviaFormRecibeJson @ enviaFormRecibeJson.js:10
submit @ ?i=1:56
ProblemDetailsError: Falta el saludo. muestraError.js:29
at consume (consume.js:16:10)
at async HTMLFormElement.submit (?i=1:55:22)
{status: 400, title: 'Falta el saludo.', type: '/erros/faltasaludo.html'} "status": 400
"title": "Falta el saludo."
"type": "/erros/faltasaludo.html"
[[Orototype]]: Object
muestraError @ muestraError.js:29
(anónimo) @ manejaErrores.js:32

D. Escenario con datos correctos

Versión para imprimir.

1. El usuario captura datos y activa la forma

Forma

2. Se activa el código del evento submit.

Forma

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

3. Se invoca el servicio, incluyendo los datos de la forma

Forma

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
 "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Request

POST /srv/valida.php HTTP/1.1 Accept: application/json, application/problem+json
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 236
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFZzsTiGaX53RKhff
Cookie: __gsas=ID=329641bd2728ff51:T=1743534294:RT=1743534294:S=ALNI_MavjA3FPY-hDE5wNZO5LxFSOVeIIQ; __test=6409d42a9d124b0fcecbb489e113453c
Host: srvvalida.rf.gd
Origin: http://srvvalida.rf.gd
Referer: http://srvvalida.rf.gd/?i=1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

saludo: hola
nombre: pp
------WebKitFormBoundaryFZzsTiGaX53RKhff
Content-Disposition: form-data; name="saludo"

hola
------WebKitFormBoundaryFZzsTiGaX53RKhff
Content-Disposition: form-data; name="nombre"

pp
------WebKitFormBoundaryFZzsTiGaX53RKhff--

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Despierta y recibe request.

4. El servicio lee los datos

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Request

POST /srv/valida.php HTTP/1.1 Accept: application/json, application/problem+json
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 236
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFZzsTiGaX53RKhff
Cookie: __gsas=ID=329641bd2728ff51:T=1743534294:RT=1743534294:S=ALNI_MavjA3FPY-hDE5wNZO5LxFSOVeIIQ; __test=6409d42a9d124b0fcecbb489e113453c
Host: srvvalida.rf.gd
Origin: http://srvvalida.rf.gd
Referer: http://srvvalida.rf.gd/?i=1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36

saludo: hola
nombre: pp
------WebKitFormBoundaryFZzsTiGaX53RKhff
Content-Disposition: form-data; name="saludo"

hola
------WebKitFormBoundaryFZzsTiGaX53RKhff
Content-Disposition: form-data; name="nombre"

pp
------WebKitFormBoundaryFZzsTiGaX53RKhff--

Memoria (Servidor)

$saludo
"hola"
$nombre
"pp"

5. El servicio comprueba que el saludo sea válido

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Memoria (Servidor)

$saludo
"hola"
$nombre
"pp"

6. Como el saludo es válido, se comprueba que el nombre sea válido

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Memoria (Servidor)

$saludo
"hola"
$nombre
"pp"

7. Como el nombre es válido, procesa los datos

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Memoria (Servidor)

$saludo
"hola"
$nombre
"pp"
$resultado
"hola pp"

8. El servicio genera la response

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Hace wait esperando response.

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Memoria (Servidor)

$saludo
"hola"
$nombre
"pp"
$resultado
"hola pp"

Response

HTTP/1.1 200 OK Server: openresty
Date: Fri, 02 May 2025 14:52:14 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: max-age=0
Expires: Fri, 02 May 2025 14:52:14 GMT
"Hola pp."

9. El servicio devuelve la response, que es recibida en el cliente

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Despierta y recibe response.

Response

HTTP/1.1 200 OK Server: openresty
Date: Fri, 02 May 2025 14:52:14 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: max-age=0
Expires: Fri, 02 May 2025 14:52:14 GMT
"Hola pp."

Memoria

respuesta
status
200
body
"Hola pp."
json
"Hola pp."

srv/valida.php

$saludo = recibeTexto("saludo");
$nombre = recibeTexto("nombre");
if (
 $saludo === false
 || $saludo === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltasaludo.html",
  title: "Falta el saludo.",
 );
}
if (
 $nombre === false
 || $nombre === ""
) {
 throw new ProblemDetails(
  status: ProblemDetails::BadRequest,
  type: "/error/faltanombre.html",
  title: "Falta el nombre.",
 );
}
$resultado =
 "{$saludo} {$nombre}.";
devuelveJson($resultado);

Devuelve response y se duerme.

10. Muestra el texto recibido en un alert

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

Memoria

respuesta
status
200
body
"Hola pp."
json
"Hola pp."

Alert

hola pp

11. Al cerrar el alert, termina el evento

index.html

const respuesta = await consume(
 enviaFormRecibeJson(
  "php/valida.php", formulario))
const json =
 await respuesta.json()
alert(json)

E. Hazlo funcionar (con videos)

  1. Prueba el ejemplo en http://srvvalida.rf.gd/.

  2. Descarga el archivo /src/srvvalida.zip y descompáctalo.

  3. Crea una cuenta de email pqra ti, por ejemplo, pepito@google.com. Si ya tienes un email, omite este paso.

  4. Crea una cuenta de GitHub usando el email anterior y selecciona el nombre de usuario unsando la parte inicial del correo electrónico, por ejemplo pepito. Si ya tienes una cuenta, omite este paso.

  5. Crea un repositorio nuevo. En el nombre del repositorio debes poner el nombre de tu sitio; por ejemplo devuelvejson

  6. Importa el proyecto de GitHub a Visual Studio Code

  7. Edita los archivos que desees.

  8. Prueba tu sitio localmente.

  9. Necesitas un hosting. En este ejemplo se muestra como usar el hosting. https://infinityfree.com/ Si no lo has usado, lo primero que tienes que hacer es entrar a registrar tu email con el botón Registrar. Si ya tienes tu email registrado, omite este paso.

  10. Crea una cuenta. Si ya tienes cuenta, entra a ella y crea un nuevo domino. En este ejemplo no se crean los archivos directamente en el hosting.

  11. Sube tus archivos al hosting usando ftp.

  12. Sube tus archivos a GitHub. En este ejemplo no hay archivo sw.js ni necesitas esperar 11 o más minutos.

F. Hazlo funcionar (texto)

  1. Prueba el ejemplo en http://srvvalida.rf.gd/.

  2. Descarga el archivo /src/srvvalida.zip y descompáctalo.

  3. Crea tu proyecto en GitHub:

    1. Crea una cuenta de email para tí, por ejemplo, pepito@google.com. Si ya tienes un email, omite este paso.

    2. Crea una cuenta de GitHub usando el email anterior y selecciona el nombre de usuario unsando la parte inicial del correo electrónico, por ejemplo pepito. Si ya tienes una cuenta, omite este paso.

    3. Crea un repositorio nuevo. En la página principal de GitHub cliquea 📘 New.

    4. En la página Create a new repository introduce los siguientes datos:

      • Proporciona el nombre de tu repositorio debajo de donde dice Repository name *.

      • Mantén la selección Public para que otros usuarios puedan ver tu proyecto.

      • Verifica la casilla Add a README file. En este archivo se muestra información sobre tu proyecto.

      • Cliquea License: None. y selecciona la licencia que consideres más adecuada para tu proyecto.

      • Cliquea Create repository.

  4. Importa el proyecto en GitHub:

    1. En la página principal de tu proyecto en GitHub, en la pestaña < > Code, cliquea < > Code y en la sección Branches y copia la dirección que está en HTTPS, debajo de Clone.

    2. En Visual Studio Code, usa el botón de la izquierda para Source Control.

      Imagen de Source Control
    3. Cliquea el botón Clone Repository.

    4. Pega la url que copiaste anteriormente hasta arriba, donde dice algo como Provide repository URL y presiona la teclea Intro.

    5. Selecciona la carpeta donde se guardará la carpeta del proyecto.

    6. Abre la carpeta del proyecto importado.

    7. Añade el contenido de la carpeta descompactada que contiene el código del ejemplo.

  5. Edita los archivos que desees.

  6. Haz clic derecho en index.html, selecciona PHP Server: serve project y se abre el navegador para que puedas probar localmente el ejemplo.

  7. Para depurar paso a paso haz lo siguiente:

    1. En el navegador, haz clic derecho en la página que deseas depurar y selecciona inspeccionar.

    2. Recarga la página, de preferencia haciendo clic derecho en el ícono de volver a cargar la página Ïmagen del ícono de recarga y seleccionando vaciar caché y volver a cargar de manera forzada (o algo parecido). Si no aparece un menú emergente, simplemente cliquea volver a cargar la página Ïmagen del ícono de recarga. Revisa que no aparezca ningún error ni en la pestañas Consola, ni en Red.

    3. Selecciona la pestaña Fuentes (o Sources si tu navegador está en Inglés).

    4. Selecciona el archivo donde vas a empezar a depurar.

    5. Haz clic en el número de la línea donde vas a empezar a depurar.

    6. En Visual Studio Code, abre el archivo de PHP donde vas a empezar a depurar.

    7. Haz clic en Run and Debug .

    8. Si no está configurada la depuración, haz clic en create a launch json file.

    9. Haz clic en la flechita RUN AND DEBUG, al lado de la cual debe decir Listen for Xdebug .

    10. Aparece un cuadro con los controles de depuración

    11. Selecciona otra vez el archivo de PHP y haz clic en el número de la línea donde vas a empezar a depurar.

    12. Regresa al navegador, recarga la página y empieza a usarla.

    13. Si se ejecuta alguna de las líneas de código seleccionadas, aparece resaltada en la pestaña de fuentes. Usa los controles de depuración para avanzar, como se muestra en este video.

  8. Sube el proyecto al hosting que elijas.

    1. Crea una nueva carpeta para crear un nuevo proyecto que estará conectado directamente al servidor web por ftp.

    2. Abre la nueva carpeta con Visual Studio Code.

    3. Tecle al mismo Mayúsculas+Control+P y selecciona SFTP: Config. Aparece un archivo de configuración de FTP. Llena los datos con la configuración de FTP de tu servidor, excepto la contraseña.

    4. Cliquea el botón de SFTP y luego haz clic en la URL de tu servidos. En la barra superior te pide la contraseña y ENTER.

    5. Pásate a la parte de archivos y coloca tus archivos.

    6. Cliquea con el botón derecho en la sección de archivos y selecciona Sync: Local -> Remote.

  9. Abre un navegador y prueba el proyecto en tu hosting.

  10. En el hosting InfinityFree, la primera vez que corres la página, puede marcar un mensaje de error, pero al recargar funciona correctamente. Puedes evitar este problema usando un dominio propio.

  11. Para subir el código a GitHub, en la sección de SOURCE CONTROL, en Message introduce un mensaje sobre los cambios que hiciste, por ejemplo index.html corregido, selecciona v y luego Commit & Push.

    Imagen de Commit & Push

G. Archivos

Haz clic en los triángulos para expandir las carpetas

H. index.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>Servicio que valida datos</title>
10
11
 <script type="module" src="js/lib/manejaErrores.js"></script>
12
13
</head>
14
15
<body>
16
17
 <h1>Servicio que valida datos</h1>
18
19
 <form id="formulario">
20
21
  <p>
22
   <label>
23
    Saludo:
24
    <!-- Como este input tiene name="saludo", su valor se recupera en el
25
     servidor con recibeTexto("saludo") -->
26
    <input name="saludo">
27
   </label>
28
  </p>
29
30
  <p>
31
   <label>
32
    Nombre:
33
    <!-- Como este input tiene name="nombre", su valor se recupera en el
34
     servidor con recibeTexto("nombre") -->
35
    <input name="nombre">
36
   </label>
37
  </p>
38
39
  <p><button type="submit">Procesa</button></p>
40
41
 </form>
42
43
 <script type="module">
44
45
  import { consume } from "./js/lib/consume.js"
46
  import { enviaFormRecibeJson } from "./js/lib/enviaFormRecibeJson.js"
47
48
  formulario.addEventListener("submit", submit)
49
50
  /**
51
   * @param {Event} event
52
   */
53
  async function submit(event) {
54
   event.preventDefault()
55
   const respuesta = await consume(
56
    enviaFormRecibeJson(
57
     "php/valida.php", formulario))
58
   const json =
59
    await respuesta.json()
60
   alert(json)
61
  }
62
63
 </script>
64
65
</body>
66
67
</html>

I. Carpeta « php »

Versión para imprimir.

A. php / valida.php

1
<?php
2
3
require_once __DIR__ . "/lib/manejaErrores.php";
4
require_once __DIR__ . "/lib/BAD_REQUEST.php";
5
require_once __DIR__ . "/lib/recibeTexto.php";
6
require_once __DIR__ . "/lib/ProblemDetailsException.php";
7
require_once __DIR__ . "/lib/devuelveJson.php";
8
9
$saludo = recibeTexto("saludo");
10
$nombre = recibeTexto("nombre");
11
12
if (
13
 $saludo === false
14
 || $saludo === ""
15
)
16
 throw new ProblemDetailsException([
17
  "status" => BAD_REQUEST,
18
  "title" => "Falta el saludo.",
19
  "type" => "/erros/faltasaludo.html"
20
 ]);
21
22
if (
23
 $nombre === false
24
 || $nombre === ""
25
)
26
 throw new ProblemDetailsException([
27
  "status" => BAD_REQUEST,
28
  "title" => "Falta el nombre.",
29
  "type" => "/errors/faltanombre.html"
30
 ]);
31
32
$resultado = "{$saludo} {$nombre}.";
33
34
devuelveJson($resultado);
35

B. Carpeta « php / lib »

1. php / lib / BAD_REQUEST.php

1
<?php
2
3
const BAD_REQUEST = 400;
4

2. php / lib / devuelveJson.php

1
<?php
2
3
require_once __DIR__ . "/devuelveResultadoNoJson.php";
4
5
function devuelveJson($resultado)
6
{
7
 $json = json_encode($resultado);
8
 if ($json === false) {
9
  devuelveResultadoNoJson();
10
 } else {
11
  header("Content-Type: application/json; charset=utf-8");
12
  echo $json;
13
 }
14
 exit();
15
}
16

3. php / lib / devuelveResultadoNoJson.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
5
function devuelveResultadoNoJson()
6
{
7
 http_response_code(INTERNAL_SERVER_ERROR);
8
 header("Content-Type: application/problem+json; charset=utf-8");
9
10
 echo '{' .
11
  "status: " . INTERNAL_SERVER_ERROR .
12
  '"title": "El resultado no puede representarse como JSON."' .
13
  '"type": "/errors/resultadonojson.html"' .
14
  '}';
15
}
16

4. php / lib / INTERNAL_SERVER_ERROR.php

1
<?php
2
3
const INTERNAL_SERVER_ERROR = 500;

5. php / lib / manejaErrores.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
require_once __DIR__ . "/ProblemDetailsException.php";
5
6
// Hace que se lance una excepción automáticamente cuando se genere un error.
7
set_error_handler(function ($severity, $message, $file, $line) {
8
 throw new ErrorException($message, 0, $severity, $file, $line);
9
});
10
11
// Código cuando una excepción no es atrapada.
12
set_exception_handler(function (Throwable $excepcion) {
13
 if ($excepcion instanceof ProblemDetailsException) {
14
  devuelveProblemDetails($excepcion->problemDetails);
15
 } else {
16
  devuelveProblemDetails([
17
   "status" => INTERNAL_SERVER_ERROR,
18
   "title" => "Error interno del servidor",
19
   "detail" => $excepcion->getMessage(),
20
   "type" => "/errors/errorinterno.html",
21
  ]);
22
 }
23
 exit();
24
});
25
26
function devuelveProblemDetails(array $array)
27
{
28
 $json = json_encode($array);
29
 if ($json === false) {
30
  devuelveResultadoNoJson();
31
 } else {
32
  http_response_code(isset($array["status"]) ? $array["status"] : 500);
33
  header("Content-Type: application/problem+json; charset=utf-8");
34
  echo $json;
35
 }
36
}
37

6. php / lib / ProblemDetailsException.php

1
<?php
2
3
require_once __DIR__ . "/INTERNAL_SERVER_ERROR.php";
4
5
/**
6
 * Detalle de los errores devueltos por un servicio.
7
 */
8
class ProblemDetailsException extends Exception
9
{
10
11
 public array $problemDetails;
12
13
 public function __construct(
14
  array $problemDetails,
15
 ) {
16
  
17
  parent::__construct(
18
   isset($problemDetails["detail"])
19
    ? $problemDetails["detail"]
20
    : (isset($problemDetails["title"])
21
     ? $problemDetails["title"]
22
     : "Error"),
23
   $problemDetails["status"]
24
    ? $problemDetails["status"]
25
    : INTERNAL_SERVER_ERROR
26
  );
27
28
  $this->problemDetails = $problemDetails;
29
 }
30
}
31

7. php / lib / recibeJson.php

1
<?php
2
3
require_once __DIR__ . "/BAD_REQUEST.php";
4
5
function recibeJson()
6
{
7
 $json = json_decode(file_get_contents("php://input"));
8
9
 if ($json === null) {
10
11
  http_response_code(BAD_REQUEST);
12
  header("Content-Type: application/problem+json; charset=utf-8");
13
14
  echo '{' .
15
   "status: " . BAD_REQUEST .
16
   '"title": "Los datos recibidos no están en formato JSON."' .
17
   '"type": "/errors/datosnojson.html"' .
18
   '}';
19
20
  exit();
21
 }
22
23
 return $json;
24
}
25

8. php / lib / recibeTexto.php

1
<?php
2
3
/**
4
 * Devuelve el texto de un parámetro enviado al
5
 * servidor por medio de GET, POST o cookie.
6
 * 
7
 * Si el parámetro no se recibe, devuelve false.
8
 */
9
function recibeTexto(string $parametro): false|string
10
{
11
 /* Si el parámetro está asignado en $_REQUEST,
12
  * devuelve su valor; de lo contrario, devuelve false.
13
  */
14
 $valor = isset($_REQUEST[$parametro])
15
  ? $_REQUEST[$parametro]
16
  : false;
17
 return $valor;
18
}
19

J. Carpeta « errors »

Versión para imprimir.

A. errors / errorinterno.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>Error interno del servidor</title>
10
11
</head>
12
13
<body>
14
15
 <h1>Error interno del servidor</h1>
16
17
 <p>Se presentó de forma inesperada un error interno del servidor.</p>
18
19
</body>
20
21
</html>

B. errors / faltanombre.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>Falta el nombre</title>
10
11
</head>
12
13
<body>
14
15
 <h1>Falta el nombre</h1>
16
17
</body>
18
19
</html>

C. errors / faltasaludo.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>Falta el saludo</title>
10
11
</head>
12
13
<body>
14
15
 <h1>Falta el saludo</h1>
16
17
</body>
18
19
</html>

D. errors / resultadonojson.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>El resultado no puede representarse como JSON</title>
10
11
</head>
12
13
<body>
14
15
 <h1>El resultado no puede representarse como JSON</h1>
16
17
 <p>
18
  Debido a un error interno del servidor, el resultado generado, no se puede
19
  recuperar.
20
 </p>
21
22
</body>
23
24
</html>

K. Carpeta « js »

Versión para imprimir.

A. Carpeta « js / lib »

1. js / lib / consume.js

1
import { ProblemDetailsError } from "./ProblemDetailsError.js"
2
3
/**
4
 * Espera a que la promesa de un fetch termine. Si
5
 * hay error, lanza una excepción.
6
 * 
7
 * @param {Promise<Response> } servicio
8
 */
9
export async function consume(servicio) {
10
 const respuesta = await servicio
11
 if (respuesta.ok) {
12
  return respuesta
13
 } else {
14
  const contentType = respuesta.headers.get("Content-Type")
15
  if (
16
   contentType !== null && contentType.startsWith("application/problem+json")
17
  )
18
   throw new ProblemDetailsError(await respuesta.json())
19
  else
20
   throw new Error(respuesta.statusText)
21
 }
22
}

2. js / lib / datosnojson.html

1
<!DOCTYPE html>
2
<html lang="es">
3
4
<head>
5
6
 <meta charset="UTF-8">
7
 <meta name="viewport" content="width=device-width">
8
9
 <title>Los datos recibidos no están en formato JSON</title>
10
11
</head>
12
13
<body>
14
15
 <h1>Los datos recibidos no están en formato JSON</h1>
16
17
</body>
18
19
</html>

3. js / lib / enviaFormRecibeJson.js

1
/**
2
 * Envía los datos de un formolario a la url usando la codificación
3
 * multipart/form-data.
4
 * @param {string} url
5
 * @param {HTMLFormElement} formulario
6
 * @param { "GET" | "POST"| "PUT" | "PATCH" | "DELETE" | "TRACE" | "OPTIONS"
7
 *  | "CONNECT" | "HEAD" } metodoHttp
8
 */
9
export function enviaFormRecibeJson(url, formulario, metodoHttp = "POST") {
10
 return fetch(
11
  url,
12
  {
13
   method: metodoHttp,
14
   headers: { "Accept": "application/json, application/problem+json" },
15
   body: new FormData(formulario)
16
  }
17
 )
18
}

4. js / lib / manejaErrores.js

1
import { muestraError } from "./muestraError.js"
2
3
/**
4
 * Intercepta Response.prototype.json para capturar errores de parseo
5
 * y asegurar que se reporten correctamente en navegadores Chromium.
6
 */
7
{
8
 const originalJson = Response.prototype.json
9
10
 Response.prototype.json = function () {
11
  // Llamamos al método original usando el contexto (this) de la respuesta
12
  return originalJson.apply(this, arguments)
13
   .catch((/** @type {any} */ error) => {
14
    // Corrige un error de Chrome que evita el manejo correcto de errores.
15
    throw new Error(error)
16
   })
17
 }
18
}
19
20
window.onerror = function (
21
  /** @type {string} */ _message,
22
  /** @type {string} */ _url,
23
  /** @type {number} */ _line,
24
  /** @type {number} */ _column,
25
  /** @type {Error} */ errorObject
26
) {
27
 muestraError(errorObject)
28
 return true
29
}
30
31
window.addEventListener('unhandledrejection', event => {
32
 muestraError(event.reason)
33
 event.preventDefault()
34
})
35

5. js / lib / muestraError.js

1
import { ProblemDetailsError } from "./ProblemDetailsError.js"
2
3
/**
4
 * Muestra los datos de una Error en la consola y en un cuadro de alerta.
5
 * @param { ProblemDetailsError | Error | null } error descripción del error.
6
 */
7
export function muestraError(error) {
8
9
 if (error === null) {
10
11
  console.error("Error")
12
  alert("Error")
13
14
 } else if (error instanceof ProblemDetailsError) {
15
16
  const problemDetails = error.problemDetails
17
18
  let mensaje =
19
   typeof problemDetails["title"] === "string" ? problemDetails["title"] : ""
20
  if (typeof problemDetails["detail"] === "string") {
21
   if (mensaje !== "") {
22
    mensaje += "\n\n"
23
   }
24
   mensaje += problemDetails["detail"]
25
  }
26
  if (mensaje === "") {
27
   mensaje = "Error"
28
  }
29
  console.error(error, problemDetails)
30
  alert(mensaje)
31
32
 } else {
33
34
  console.error(error)
35
  alert(error.message)
36
37
 }
38
39
}

6. js / lib / ProblemDetailsError.js

1
export class ProblemDetailsError extends Error {
2
3
 /**
4
  * Detalle de los errores devueltos por un servicio.
5
  * Crea una instancia de ProblemDetailsError.
6
  * @param {object} problemDetails Objeto con la descripcipon del error.
7
  */
8
 constructor(problemDetails) {
9
10
  super(typeof problemDetails["detail"] === "string"
11
   ? problemDetails["detail"]
12
   : (typeof problemDetails["title"] === "string"
13
    ? problemDetails["title"]
14
    : "Error"))
15
16
  this.problemDetails = problemDetails
17
18
 }
19
20
}

L. jsconfig.json

1
{
2
 "compilerOptions": {
3
  "checkJs": true,
4
  "strictNullChecks": true,
5
  "target": "ES6",
6
  "module": "Node16",
7
  "moduleResolution": "Node16",
8
  "lib": [
9
   "ES2017",
10
   "WebWorker",
11
   "DOM"
12
  ]
18
}

M. Resumen