Vamos a ver cómo crear un servidor web en el lenguaje de programación Go, también conocido como Golang. Lo que haremos será crear un servidor simple que responderá a peticiones GET, es decir, a las direcciones URL y que servirá archivos.
Explicado de otra manera, nuestro server hará esto:
No necesitaremos librerías extras ni frameworks, pues el maravilloso lenguaje provee todo lo que necesitamos.
Por cierto, si no lo tienes, mira cómo instalar Go en Windows.
Este tutorial es completo, explicaré a detalle cada cosa, te aseguro que aunque es un poco largo te enseñará lo robusto que es el lenguaje Go en cuanto a la web.
¿Interesado en algo más robusto? conoce mux.
Todo lo que usaremos para este servidor web está dentro del paquete net/http. Go es un lenguaje excelente para la web, y sus librerías incluidas por defecto hacen el trabajo muy bien.
A diferencia de Apache u otros servidores que ya vienen configurados por defecto, en Go podemos cambiar el puerto en una simple línea de código; nada de archivos de configuración.
Pero no sólo eso, sino que podemos especificar en cuál IP escuchar. En el código de abajo verás que a veces definimos el puerto como cadena, así:
":8080"
Y tú dirás, ¿no el puerto debería ser un entero, o bueno, no debería ir sin los dos puntos? La respuesta es que sí, pero no estamos especificando el puerto, sino la dirección. De otro modo, esa dirección podría quedar así:
"127.0.0.1:8080"
o también "localhost:8080"
, de igual manera podría quedar como "192.168.1.76:8080"
en donde 192.168.1.76 es la dirección del servidor.
¿Entonces, cuál es la diferencia? bueno, si especificamos únicamente :8080
entonces escuchamos en localhost, en la ip pública y en 127.0.0.1.
En caso de que sólo especifiquemos, por ejemplo, 127.0.0.1:8080
, el servidor sólo respondería a peticiones que se hagan en la computadora que es al mismo tiempo el servidor, no desde otras. Tómalo en cuenta por si no quieres exponer tu servidor.
En resumen, si quieres que cualquier PC se conecte al servidor sabiendo su IP, especifica únicamente el puerto con :8080
.
Por cierto, :8080
es un ejemplo, pero el puerto puede ser el que tú quieras, siempre y cuando esté desocupado.
El código para tener un servidor HTTP es este:
/*
Ejemplo 1: hola mundo en servidor HTTP con Go
@author parzibyte
*/
package main
import (
"fmt"// Imprimir en consola
"io"// Ayuda a escribir en la respuesta
"log"//Loguear si algo sale mal
"net/http"// El paquete HTTP
)
func main() {
http.HandleFunc("/hola", func(w http.ResponseWriter, peticion *http.Request) {
io.WriteString(w, "Solicitaste hola")
})
direccion := ":8080" // Como cadena, no como entero; porque representa una dirección
fmt.Println("Servidor listo escuchando en " + direccion)
log.Fatal(http.ListenAndServe(direccion, nil))
}
Lo que hacemos es llamar a HandleFunc, le ponemos la ruta y la función que lo manejará. En el caso de que visiten /hola se mostrará un simple mensaje que dirá “Solicitaste hola”.
De esa manera podemos definir más rutas, y más manejadores. Por cierto, si quieres usar el mismo manejador para dos rutas, define la función primero y guárdala, para más tarde mandarla como argumento.
Después de definir todas nuestras rutas, imprimimos un mensaje para decir que ya está listo nuestro servidor; y llamamos al método http.ListenAndServe
; como primer argumento le ponemos la dirección y como segundo sería un manejador que no necesitamos por el momento (más información aquí).
Ahora compilamos y ejecutamos, visitamos la dirección y ya está nuestro servidor web con Go:
Como lo dije, podemos definir más rutas y manejadores distintos. No importa cómo sea la ruta o qué tan larga sea. Aquí vemos otro ejemplo:
/*
Ejemplo 2: otras rutas en servidor HTTP con Go
@author parzibyte
*/
package main
import (
"fmt"// Imprimir en consola
"io"// Ayuda a escribir en la respuesta
"log"//Loguear si algo sale mal
"net/http"// El paquete HTTP
)
func main() {
http.HandleFunc("/hola", func(w http.ResponseWriter, peticion *http.Request) {
io.WriteString(w, "Solicitaste hola")
})
http.HandleFunc("/ruta/un/poco/larga", func(w http.ResponseWriter, peticion *http.Request) {
io.WriteString(w, "Solicitaste ruta/un/poco/larga")
})
direccion := ":8080" // Como cadena, no como entero; porque representa una dirección
fmt.Println("Servidor listo escuchando en " + direccion)
log.Fatal(http.ListenAndServe(direccion, nil))
}
De esta manera estamos sirviendo otra ruta. Más tarde traeré un tutorial para rutas en Go, con expresiones regulares, como lo hicimos con PHP.
Si visitamos ahora la ruta, se ve esto:
La mayoría de veces usamos la URL para mandar datos. Por ejemplo: sitio.com?id=1&nombre=parzibyte
Con Go, podemos leer los parámetros GET fácilmente. Veamos este ejemplo de lectura de parámetro de la url:
/*
Ejemplo 3: parámetros de la url
@author parzibyte
*/
package main
import (
"fmt" // Imprimir en consola
"io" // Ayuda a escribir en la respuesta
"log" //Loguear si algo sale mal
"net/http" // El paquete HTTP
)
func main() {
http.HandleFunc("/saludo", func(w http.ResponseWriter, peticion *http.Request) {
listaDeNombres, existen := peticion.URL.Query()["nombre"]
// Imprimir para depurar
fmt.Printf("%v\n", listaDeNombres)
if existen {
io.WriteString(w, "Hola, "+listaDeNombres[0])
} else {
io.WriteString(w, "Hola, desconocido")
}
})
direccion := ":8080" // Como cadena, no como entero; porque representa una dirección
fmt.Println("Servidor listo escuchando en " + direccion)
log.Fatal(http.ListenAndServe(direccion, nil))
}
Para leer un parámetro utilizamos:
peticion.URL.Query()["clave"]
En donde clave es la clave que queremos leer. Esta función devolverá false como segunda respuesta si la clave no existe; por eso es que primero comprobamos si existen.
Otra cosa, ¿por qué es listaDeNombres
y no simplemente nombre
? es porque devolverá un arreglo con los valores que tengan esa clave. Por ejemplo, si la URL es:
http://localhost:8080/saludo?nombre=Parzibyte
Entonces devolverá [Parzibyte]
Pero si la URL es:
http://localhost:8080/saludo?nombre=Parzibyte&nombre=Maggie
Sí, dos veces la misma clave, entonces devuelve:
[Parzibyte, Maggie]
Esto permitiría mandar una lista de valores con la misma clave, pero ese no es el punto, por eso leemos el primer elemento: listaDeNombres[0]
Lo puedes ejecutar y dependiendo de los valores te dirá desconocido o tu nombre:
Un servidor web sirve archivos, y no se limita a responder a rutas. Servir una carpeta pública con Go es muy fácil. Veamos este ejemplo:
/*
Ejemplo 4: Servir archivos, por ejemplo imágenes, HTML, vídeos, etcétera
@author parzibyte
*/
package main
import (
"fmt" // Imprimir en consola
"log" //Loguear si algo sale mal
"net/http" // El paquete HTTP
)
func main() {
http.Handle("/", http.FileServer(http.Dir("./archivos")))
direccion := ":8080" // Como cadena, no como entero; porque representa una dirección
fmt.Println("Servidor listo escuchando en " + direccion)
log.Fatal(http.ListenAndServe(direccion, nil))
}
En una línea servimos un directorio:
http.Handle("/", http.FileServer(http.Dir("./archivos")))
En este caso, cuando visitemos la raíz, se servirá todo lo que hay en la carpeta archivos. Compilamos y visitamos la página; veremos que muestra todo lo que hay en la carpeta:
En ese caso estamos sirviendo a partir de la raíz, en la url:
localhost:8080/
¿Qué pasa si queremos servir la carpeta pero en otra ruta? por ejemplo, en la url:
localhost:8080/archivos/
Eso no es posible con el código de arriba, porque Go sirve relativamente a la ruta del archivo. Si queremos servir en otra ruta, el código se complica pero sólo un poco:
/*
Ejemplo 5: Servir archivos, por ejemplo imágenes, HTML, vídeos, etcétera en
una ruta que no es la raíz
@author parzibyte
*/
package main
import (
"fmt" // Imprimir en consola
"log" //Loguear si algo sale mal
"net/http" // El paquete HTTP
)
func main() {
http.Handle("/archivos/", http.StripPrefix("/archivos/", http.FileServer(http.Dir("./archivos"))))
direccion := ":8080" // Como cadena, no como entero; porque representa una dirección
fmt.Println("Servidor listo escuchando en " + direccion)
log.Fatal(http.ListenAndServe(direccion, nil))
}
Lo que hacemos es quitar un prefijo de la ruta. Para que se te haga fácil, lo mismo que pongas en la ruta ponlo en StripPrefix
y listo.
Cabe mencionar que el argumento que se pasa a http.Dir
puede ser una ruta absoluta si así se requiere.
Una cosa es la ruta en donde se sirve, y otra es la carpeta que se sirve. El nombre y la ruta no deben ser obligatoriamente las mismas; es decir, podríamos servir en la ruta publico la carpeta llamada archivos:
http.Handle("/publico/", http.StripPrefix("/publico/", http.FileServer(http.Dir("./archivos"))))
En ese caso servimos en la ruta publico, quitamos el prefijo publico, pero en realidad servimos lo que hay en archivos.
Puedes servir una carpeta que no es relativa al archivo. Así:
http.Handle("/publico/", http.StripPrefix("/publico/", http.FileServer(http.Dir(`D:`))))
De esa manera exponemos el disco local D en la ruta /publico; por cierto, no lo hagas en casa.
Quedan pendientes más tutoriales que traeré pronto.
Actualización: aquí hay un tutorial con enrutador y middleware usando mux.
Por ejemplo para leer datos de un formulario, parsear rutas, comunicarse a través de JSON, renderizar plantillas y otras cosas interesantes.
Te invito a leer más sobre el módulo HTTP en Go: https://golang.org/pkg/net/http/
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.
Ver comentarios
Buenas tardes, una pregunta, tengo una pagina con acceso de login y tambien para logearse, esto hecho en golang con mysql, la pregunta es: 1- con localhost:8089 me funciona perfecto al grabar el logeo en mysql , opara entrar mediante login, pero como hago para que desde culaquier parte en internet se puedan loguear o entrar con el login, no he podido lograrlo, como puede ser la situacion
Hola. Para consultas escríbame en https://parzibyte.me#contacto
Saludos!