En esta tercera entrega de Construyendo Servidores Web desde Cero reescribimos y mejoramos el sistema de enrutamiento para transformar un conjunto de switch anidados en un componente limpio y escalable: un router dedicado que gestiona métodos HTTP y paths de forma centralizada.
Resumen rápido de lo que teníamos: un servidor HTTP básico que escuchaba peticiones, enrutamiento por path implementado con switch, múltiples switch anidados difíciles de mantener y sin una forma limpia de registrar rutas de forma dinámica.
El enfoque con switch anidados conduce a problemas claros. La lógica de rutas queda dispersa entre varias funciones, añadir nuevos métodos HTTP obliga a crear nuevas funciones, no se pueden registrar rutas programáticamente y la organización es pobre en comparación con frameworks que permiten definir rutas con una sola línea.
Cuando usamos frameworks como Express o Gin lo que sucede internamente es simple: las rutas se almacenan en una estructura de datos y las peticiones entrantes se comparan contra esa tabla para decidir qué handler ejecutar. Vamos a construir algo similar pero ligero y didáctico en Go.
Diseño del router: necesitamos poder registrar rutas combinando método HTTP más path más manejador, resolver la ruta correcta para una petición entrante y ejecutar ese manejador o devolver 404. Una estructura eficiente es una tabla de tablas, es decir map por método que contiene map por path hacia el handler.
Componente router: creamos un tipo HandlerFunc que recibe writer y request y una estructura Router con un campo routes map[method]map[path]HandlerFunc. NewRouter inicializa la estructura y devuelve un puntero al router.
Métodos de registro: añadimos addRoute para insertar en la tabla y métodos de conveniencia GET POST PUT PATCH DELETE que llaman a addRoute. Con esto la API para registrar rutas queda muy clara y similar a frameworks: router.GET(/hello, helloHandler) router.POST(/users, createUserHandler).
Resolución de rutas: resolveRoute recibe la request, extrae método y path y busca en la tabla correspondiente; si no encuentra método o path devuelve error. ServeHTTP del router implementa http.Handler, llama a resolveRoute y si hay handler lo ejecuta o bien responde con 404 si no hay coincidencia. De este modo la responsabilidad de routing queda totalmente separada del servidor.
Integración en el servidor: la estructura Server ahora contiene un campo Router y ServeHTTP delega en s.Router.ServeHTTP. NewServer crea una instancia de Router y Start arranca un http.Server con Handler apuntando al servidor, manteniendo la delegación limpia y la responsabilidad bien separada.
Handlers y firma: actualizamos las funciones manejadoras para aceptar tanto http.ResponseWriter como *http.Request de manera que tengan acceso completo a la petición. Por ejemplo welcome escribe Welcome to our Go Webserver en la respuesta, hello responde Hello World y getTime obtiene la hora actual desde time.Now y la formatea para devolverla en la respuesta.
Registro de rutas en main: en setupRoutes registramos las rutas de forma expresiva y legible: s.Router.GET(/, welcome) s.Router.GET(/hello, hello) s.Router.GET(/goodbye, goodbye) s.Router.GET(/time, getTime) s.Router.POST(/, post_welcome) s.Router.POST(/hello, post_hello) s.Router.POST(/messages, messages) s.Router.POST(/time, post_getTime). El resultado es un código muy cercano a la experiencia de Express o Gin.
Pruebas rápidas: arranca el servidor y prueba rutas GET y POST con curl como por ejemplo curl https://localhost:3000/ curl https://localhost:3000/hello curl -X POST https://localhost:3000/messages. Las rutas definidas deben responder correctamente y las rutas desconocidas devolver un mensaje de ruta no encontrada con estado 404.
Logros principales: registro de rutas limpio y centralizado, separación de responsabilidades entre servidor y router, reconocimiento de métodos HTTP distintos, manejo de errores 404 y arquitectura escalable que facilita añadir nuevas rutas.
Limitaciones actuales: no hay rutas dinámicas tipo /users/:id ni extracción de parámetros en el path, no existe soporte de middleware para logging, autenticación o CORS, y las respuestas de error son básicas y en texto plano en lugar de JSON estructurado.
Próximos pasos: en la siguiente parte trabajaremos rutas dinámicas y extracción de parámetros para soportar patrones como /users/:id y /products/:category/:id, lo que nos acercará a un router apto para APIs reales.
Desafío: añade rutas nuevas usando diferentes métodos. Prueba a crear DELETE para /users y PUT para /messages y observa lo sencillo que resulta comparado con el enfoque de switch anidados.
En Q2BSTUDIO somos una empresa de desarrollo de software que crea aplicaciones a medida y software a medida orientado a resultados. Combinamos experiencia en desarrollo con soluciones de inteligencia artificial e implementaciones cloud. Si necesitas una plataforma a medida podemos ayudarte con diseño, desarrollo y despliegue, además de ofrecer servicios de ciberseguridad, pentesting, servicios cloud aws y azure, inteligencia de negocio y power bi.
Si tu proyecto requiere una aplicación multiplataforma o software a medida visita nuestra página de desarrollo de aplicaciones a medida para ver cómo trabajamos. También ofrecemos soluciones de ia para empresas, agentes IA y servicios de Business Intelligence enfocados en decisiones basadas en datos.
Palabras clave integradas naturalmente para mejorar posicionamiento: aplicaciones a medida, software a medida, inteligencia artificial, ciberseguridad, servicios cloud aws y azure, servicios inteligencia de negocio, ia para empresas, agentes IA, power bi. Contacta con Q2BSTUDIO para llevar tu servidor o API al siguiente nivel con arquitectura limpia y escalable.