Introducción
Un puesto comunitario tiene una pequeña colección de objetos perdidos. Su página preparada puede mostrar objetos, pero la API de ejemplo solo devuelve un registro. Conectarás registros reales de Go con respuestas de lista y de detalle para que los visitantes encuentren el lugar de recogida correcto.
Debes conocer las funciones, estructuras, slices, mapas y módulos de Go, además de las rutas básicas de terminal. Aquí se presentan HTTP, JSON y las pruebas de manejadores. El entorno del navegador incluye Go y una página preparada. Esta unidad comienza de forma independiente y no necesita archivos de otro laboratorio. Calcula unos 55 minutos, incluida la lectura y los errores habituales; es una estimación del autor.
Seleccionar un objeto sin alterar la lista
En este paso, mantén una colección funcional de una tarjeta mientras añades detalles reales de los objetos. Comienza en la pestaña Workspace. Su shell entra en /home/labex/project/lost-found; el siguiente cd recupera esa ubicación si vuelves más tarde.
Go está instalado en /usr/local/go/bin. export añade ese directorio a la ruta de búsqueda de comandos del shell, conservando el $PATH existente. Esto se aplica a la sesión de terminal actual; repítelo en un shell nuevo. No cambia ninguna configuración global del shell.
cd /home/labex/project/lost-found
export PATH=/usr/local/go/bin:$PATH
curl envía una solicitud. -i incluye las cabeceras de respuesta, y las comillas simples mantienen unida la URL. GET pide leer un recurso. /api/items identifica la colección; 127.0.0.1:8080 es el servicio en esta misma máquina. Utiliza primero el ejemplo normal:
curl -i 'http://127.0.0.1:8080/api/items'
Espera 200 OK, una cabecera Content-Type: application/json y un array que contiene LF-101, el paraguas azul de la biblioteca. El estado HTTP describe el resultado de la solicitud; el cuerpo transporta los datos. JSON usa corchetes para los arrays y llaves para los objetos. Un 200 por sí solo no demuestra que la colección esté completa. Cambia a Lost and Found y observa su única tarjeta; después vuelve a Workspace.
cat muestra un archivo sin editarlo. Lee los registros de origen, la estructura de entrada y tu pequeño manejador inicial:
cat items.json
cat main.go
cat handler.go
Hay seis registros. Item agrupa ID, Name, Category y Location; los campos exportados que empiezan por mayúscula se pueden codificar. Las etiquetas como `json:"id"` eligen claves JSON en minúsculas. main.go carga la colección, sirve la página preparada y pasa su slice de objetos a catalogHandler. Conserva main.go, go.mod, items.json, serve.py y web/; edita únicamente handler.go y los archivos o pruebas de Go adicionales.
Antes de cambiar el código, predice la diferencia entre una solicitud de LF-101 y otra de LF-103. Ambos nombres son Blue umbrella, pero los lugares son Library desk y Cafe counter. ¿Qué campo puede seleccionar un único registro? Ahora solicita el segundo ID:
curl -i 'http://127.0.0.1:8080/api/items/LF-103'
El manejador inicial sigue devolviendo su array del primer registro. Es una respuesta recibida, pero no un detalle correcto. Tu tarea es devolver el objeto seleccionado por ID manteniendo disponible la respuesta de lista actual. No lo resuelvas devolviendo el primer objeto para todas las rutas.
Un manejador es código de Go que se llama para cada solicitud. r *http.Request apunta a la solicitud entrante. w http.ResponseWriter es el destino: Header establece las cabeceras y las escrituras envían el cuerpo. http.Handler describe ese comportamiento; http.HandlerFunc adapta una función a él. La función anónima devuelta recuerda los items suministrados. No necesitas escribir un analizador HTTP.
Antes de editar, guarda una copia. cp copia el archivo; la extensión .backup evita deliberadamente .go, que crearía definiciones duplicadas cuando Go compile el paquete.
cp handler.go handler.go.backup
Abre el archivo real en el editor de terminal. El ajuste LC_ALL=C.UTF-8, limitado al comando, hace que nano interprete el texto como UTF-8, incluidos los caracteres acentuados. Solo afecta a esta invocación del editor, no a la configuración global del shell:
LC_ALL=C.UTF-8 nano handler.go
En nano, Ctrl significa mantener Control mientras pulsas la tecla indicada. Usa las flechas para moverte. Pulsa Ctrl+W, escribe catalogHandler y después Enter para encontrar la función; Ctrl+C cancela una búsqueda sin terminar. Edita escribiendo normalmente y con Backspace y Delete. Ctrl+K corta la línea actual; úsalo solo en una línea que quieras reemplazar. Las etiquetas de atajos inferiores usan ^ para representar Control. Estás editando el manejador existente, no creando un segundo programa.
Implementa las decisiones siguientes dentro de la función devuelta, después de su cabecera de tipo de contenido existente. Trabaja en una rama cada vez:
Primero, lee r.URL.Path. Si es igual a /api/items, conserva la codificación del array items[:1] del ejemplo inicial y retorna inmediatamente. Así mantienes la lista normal de una tarjeta mientras trabajas en los detalles.
Después, en el caso contrario, reconoce el prefijo /api/items/. Añade la importación de la biblioteca estándar strings junto a las existentes. strings.HasPrefix(path, prefix) comprueba un prefijo y strings.TrimPrefix(path, prefix) lo elimina. Guarda el ID restante en una variable local.
A continuación, recorre el slice suministrado con for _, item := range items. Compara item.ID con ese ID solicitado, no con el nombre. Cuando coincida, codifica ese objeto item y retorna inmediatamente. json.NewEncoder(w).Encode(value) escribe el valor Go elegido como JSON; un slice se convierte en array y una estructura, en objeto. El bucle debe examinar todos los registros, no solo items[0].
Después, si ningún ID coincidió o la ruta queda fuera de esas rutas, establece 404 antes de escribir un error JSON. w.WriteHeader(http.StatusNotFound) establece el estado. Un pequeño valor de error compatible con JSON es map[string]string{"error": "item not found"}; la cadena error no debe estar vacía. Codifica ese mapa. No escribas otra lista después de un error.
La primera escritura de una respuesta normalmente confirma 200, así que establece un estado diferente antes de codificar. Encode devuelve un error: nil significa que no hubo fallo. En este ejemplo, todos los valores son cadenas compatibles con JSON. Un cliente desconectado todavía puede hacer que falle la escritura; enviar una segunda respuesta una vez iniciado el cuerpo no puede reparar la conexión. Los retornos anticipados también evitan añadir dos respuestas.
Guarda con Ctrl+O, confirma el nombre existente handler.go con Enter y sal con Ctrl+X. Revisa el archivo guardado:
cat handler.go
Si abriste el archivo equivocado, sal sin guardar: Ctrl+X y después N si te pregunta si quieres guardar. Ejecuta pwd para comprobar tu ubicación, recupera el primer cd y vuelve a abrir el nombre correcto. Si la solicitud de guardado muestra un nombre que no pretendías usar, Ctrl+C la cancela. No sobrescribas los datos de origen para compensar un error de edición.
gofmt -w formatea el archivo Go indicado en su ubicación. El serve.py preparado compila tu código antes de sustituir únicamente el servidor registrado de este espacio. No proporciona ninguna implementación del manejador. Un error de compilación conserva el proceso anterior que funcionaba, por lo que una respuesta antigua del navegador no demuestra que el código nuevo se haya compilado.
gofmt -w handler.go
python3 serve.py
Espera Lost and Found ready on port 8080. Si el formato o la compilación fallan, lee el nombre del archivo y el número de línea. Vuelve a abrir nano handler.go, encuentra la expresión cercana con Ctrl+W, repara solo esa región, guarda, sal y repite ambos comandos. Conserva la lógica que ya funciona; no restaures la copia inicial sobre avances posteriores. Si aparece No such file, recupera el primer cd. Los diagnósticos del servicio están en .work/server.log, que puedes leer con cat .work/server.log; un error al escribir no es motivo para reiniciar la VM.
curl -i 'http://127.0.0.1:8080/api/items/LF-103'
Espera un objeto cuyo id sea LF-103 y cuya location sea Cafe counter, no un array ni el paraguas de la biblioteca. Comprueba que la lista normal sigue devolviendo su array de muestra:
curl -i 'http://127.0.0.1:8080/api/items'
Has añadido selección manteniendo el formato de colección de la página que funciona. El siguiente paso amplía deliberadamente esa lista a todos los registros.
Devolver la colección completa
En este paso, convierte la lista de un registro en un catálogo completo de solo lectura sin perder los detalles que escribiste. Comienza en Workspace, en /home/labex/project/lost-found.
Antes de editar, lee la expresión del slice en la rama de colección. items[:1] selecciona un prefijo que termina antes del índice uno, por lo que oculta los otros cinco registros. Predice la respuesta si el codificador recibe los propios items. A diferencia de un array fijo de seis objetos, ese argumento también funciona para otro slice suministrado.
LC_ALL=C.UTF-8 nano handler.go
Busca items[:1] con Ctrl+W y Enter. Cambia solo el valor codificado de la colección por el slice de entrada completo. Conserva la cabecera JSON, el bucle de detalles, el error de ID inexistente y los retornos anticipados. Guarda en el mismo archivo con Ctrl+O y Enter y después Ctrl+X.
Una colección vacía debe codificarse como [], nunca como null. Un slice vacío distinto de nil se codifica como []; uno nil se codificaría como null. La entrada vacía independiente de esta unidad es []Item{}. No sustituyas un resultado vacío por un error de objeto inexistente: una lista y un detalle tienen significados distintos. El orden del array y de las claves del objeto puede variar, pero el nombre, la categoría y el lugar pertenecientes a cada ID deben mantenerse unidos.
gofmt -w handler.go
python3 serve.py
Solicita la colección y después los dos paraguas mediante sus ID respectivos:
curl -i 'http://127.0.0.1:8080/api/items'
curl -i 'http://127.0.0.1:8080/api/items/LF-101'
curl -i 'http://127.0.0.1:8080/api/items/LF-103'
Espera los seis registros completos en el array. LF-101 sigue correspondiendo a Library desk y LF-103 a Cafe counter, aunque ambos nombres sean Blue umbrella. Si algún detalle cambió de lugar, revisa la comparación de ID y qué valor del bucle codificas.
La siguiente solicitud utiliza deliberadamente un ID inexistente. Espera HTTP 404 y un objeto JSON con una cadena error no vacía, en lugar de una lista vacía. Normalmente curl sigue terminando correctamente cuando recibe una respuesta de error HTTP: recibir una respuesta es distinto de no poder conectarse.
curl -i 'http://127.0.0.1:8080/api/items/no-such-id'
Este manejador todavía no filtra ni impone el método HTTP; el siguiente laboratorio guiado introduce esos comportamientos. No consideres que una página de inicio normal o un 200 cualquiera demuestren que funciona un endpoint de detalle. Ahora has conservado la lista y los detalles juntos mientras cambiabas el alcance de la colección.
Escribir una prueba que distinga identidades
En este paso, diseña tu propia prueba específica del manejador sin ejecutar un segundo servidor en vivo y después utiliza la página real. Comienza en Workspace, en el mismo directorio.
Decide primero qué debe detectar la prueba: un manejador podría devolver el primer nombre coincidente en lugar del ID solicitado. Elige dos registros pequeños propios con el mismo nombre pero distintos ID y lugares, ajenos a la colección de la página. Solicita el segundo ID. Escribe qué estado, ID y lugar deben devolverse. Estos datos de prueba independientes hacen fallar una respuesta fija del servicio y permiten que la prueba sea útil más allá de los seis registros preparados.
Go descubre pruebas en archivos terminados en _test.go. Una función de prueba comienza por Test y recibe t *testing.T. Crea tu archivo de prueba con nano:
LC_ALL=C.UTF-8 nano handler_test.go
Comienza con package main. Importa encoding/json, net/http/httptest y testing. Añade una función como func TestSecondIdentity(t *testing.T) { ... }, sustituyendo los puntos por tus instrucciones de prueba. El cuerpo de la función tiene cuatro partes:
Primero, construye tus datos de prueba []Item de dos registros utilizando los nombres de campos exportados de main.go. Pásalos a catalogHandler, en lugar de cargar o modificar items.json.
Después, crea una solicitud del detalle del segundo registro. httptest.NewRequest("GET", path, nil) construye una solicitud; el último argumento indica que no hay cuerpo de solicitud. httptest.NewRecorder() devuelve un grabador de respuestas. Llamar a ServeHTTP(recorder, request) en tu manejador ejecuta el manejador real y captura sus cabeceras, estado y cuerpo.
A continuación, declara var got Item. Decodifica con json.Unmarshal(recorder.Body.Bytes(), &got). &got es la dirección donde se almacenarán los campos. Comprueba primero el error devuelto: un JSON no válido no debe confundirse con una estructura vacía válida. Por ejemplo, if err != nil { t.Fatal(err) } detiene la prueba con un fallo legible.
Después, comprueba el estado y la identidad esperados. recorder.Code contiene el estado, y got.ID y got.Location contienen los campos decodificados. Compáralos con el segundo registro de prueba. Usa if y t.Fatalf para fallar mostrando los valores reales. %d muestra un número y %+v muestra los campos de la estructura. No compruebes únicamente que exista algún JSON.
Usa los ID y lugares elegidos de forma coherente en la solicitud y las aserciones. Mantén los datos de prueba, la ejecución y las aserciones en la misma función de prueba para que el razonamiento sea fácil de seguir. Guarda con Ctrl+O y Enter y después Ctrl+X. El entorno no proporciona una prueba que ya apruebe.
go test -v . ejecuta las pruebas de este paquete: -v muestra los nombres y . selecciona el paquete actual.
gofmt -w handler_test.go
go test -v .
Espera el nombre de tu prueba seguido de PASS. Un fallo debe mostrar el estado o el objeto real. Si falla, decide si está mal la relación esperada de tus datos de prueba o el manejador; corrige solo esa parte con nano y vuelve a ejecutar la prueba. Un error de compilación identifica el archivo y la línea que debes revisar. Reconstruye con python3 serve.py después de corregir el manejador para que el navegador también use el código actual. Conserva al menos una prueba de manejador Go que apruebe en este espacio; la comprobación final ejecuta tus pruebas de forma aislada y comprueba la API con otros registros de entrada suministrados.
Cambia a Lost and Found y recarga. Espera 6 items found. Abre View item en LF-103 y lee su referencia y lugar de recogida; después elige Back to collection. Los dos paraguas deben seguir siendo distinguibles. Esta unidad permite explorar la colección y sus detalles; el siguiente laboratorio guiado añade controles de búsqueda junto con su filtrado en la API.

Busca las seis tarjetas y sus referencias individuales. La página consume tu API en ejecución y no contiene una colección de respaldo. Vuelve a Workspace si necesitas una corrección. Ahora tienes un catálogo utilizable y una prueba cuyos datos y aserciones elegiste para detectar un error real de identidad.
Resumen
Codificaste registros de Go como JSON, seleccionaste respuestas de lista y detalle según la ruta, mantuviste los nombres repetidos vinculados a sus propios ID y devolviste un estado real para un objeto inexistente. También escribiste una prueba aislada con httptest y utilizaste la página en funcionamiento. Las búsquedas y el tratamiento más estricto de errores son la tarea independiente del siguiente laboratorio guiado.



