Un Resumen del Manejo de solicitudes en Go
Procesar solicitudes HTTP con Go se trata principalmente de dos cosas: ServeMuxes y Manejadores.
Un ServeMux es esencialmente un enrutador de solicitud HTTP (o multiplexor). Compara las solicitudes entrantes con una lista de rutas de URL predefinidas y llama al controlador asociado para la ruta cada vez que se encuentra una coincidencia.Los manejadores
son responsables de escribir encabezados y cuerpos de respuesta. Casi cualquier objeto puede ser un manejador, siempre que satisfaga la interfaz http.Handler
. En términos sencillos, eso simplemente significa que debe tener un método ServeHTTP
con la siguiente firma:
ServeHTTP(http.ResponseWriter, *http.Request)
El paquete HTTP de Go se envía con algunas funciones para generar controladores comunes, como FileServer
NotFoundHandler
y RedirectHandler
. Comencemos con un ejemplo simple pero artificial:
Veamos esto rápidamente:
- En la función
main
utilizamos la funciónhttp.NewServeMux
para crear un ServeMux vacío. - Luego usamos la función
http.RedirectHandler
para crear un nuevo controlador. Este controlador 307 redirige todas las solicitudes que recibe ahttp://example.org
. - A continuación, usamos la función
mux.Handle
para registrar esto con nuestro nuevo ServeMux, por lo que actúa como el controlador para todas las solicitudes entrantes con la ruta de URL/foo
. - Finalmente creamos un nuevo servidor y comenzamos a escuchar las solicitudes entrantes con la función
http.ListenAndServe
, pasando nuestro ServeMux para que coincida con las solicitudes.
Ir adelante y ejecutar la aplicación:
Y visita http://localhost:3000/foo
en el navegador. Debe encontrar que su solicitud se redirige correctamente.
El ojo de águila de ustedes podría haber notado algo interesante: La firma para la función ListenAndServe es ListenAndServe(addr string, handler Handler)
, pero pasamos un ServeMux como segundo parámetro.
Pudimos hacer esto porque el tipo ServeMux también tiene un método ServeHTTP
, lo que significa que también satisface la interfaz del Controlador.
Para mí, simplifica las cosas pensar en un ServeMux como un tipo especial de manejador, que en lugar de proporcionar una respuesta en sí misma pasa la solicitud a un segundo manejador. Esto no es tanto un salto como suena a primera vista: encadenar a los manipuladores es bastante común en Go.
Controladores Personalizados
Vamos a crear un controlador personalizado que responde con la hora local actual en un formato determinado:
El código exacto aquí no es demasiado importante.
Todo lo que realmente importa es que tenemos un objeto (en este caso es una estructura timeHandler
, pero también podría ser una cadena o función o cualquier otra cosa), y hemos implementado un método con la firma ServeHTTP(http.ResponseWriter, *http.Request)
en él. Eso es todo lo que necesitamos para hacer un controlador.
Vamos a incrustar este en un ejemplo concreto:
En la función main
inicializamos la función timeHandler
exactamente de la misma manera que inicializaríamos cualquier estructura normal, utilizando la función &
símbolo para producir un puntero. Y luego, como en el ejemplo anterior, usamos la función mux.Handle
para registrar esto con nuestro ServeMux.
Ahora, cuando ejecutemos la aplicación, el ServeMux pasará cualquier solicitud de /time
directamente a nuestro método timeHandler.ServeHTTP
.
Adelante, inténtalo: http://localhost:3000/time
.
Tenga en cuenta también que podríamos reutilizar fácilmente el timeHandler
en múltiples rutas:
Funciona como Manejadores
Para casos simples (como el ejemplo anterior) definir nuevos tipos personalizados y métodos ServeHTTP se siente un poco detallado. Veamos un enfoque alternativo, donde aprovechamos el tipo http.HandlerFunc
de Go para obligar a una función normal a satisfacer la interfaz del Controlador.
Cualquier función que tenga la firma func(http.ResponseWriter, *http.Request)
se puede convertir en un tipo HandlerFunc. Esto es útil porque los objetos HandleFunc vienen con un método incorporado ServeHTTP
que, de forma bastante inteligente y conveniente, ejecuta el contenido de la función original.
Si eso suena confuso, intente echar un vistazo al código fuente relevante. Verás que es una forma muy sucinta de hacer que una función satisfaga la interfaz del Controlador.
Reproduzcamos la aplicación del controlador de tiempo utilizando esta técnica:
De hecho, convertir una función a un tipo HandlerFunc y luego agregarla a un ServeMux como este es tan común que Go proporciona un acceso directo: el método mux.HandleFunc
.
Esto es lo que la función main()
se habría visto si hubiéramos utilizado este acceso directo en su lugar:
La mayor parte del tiempo usando una función como manejador como esta funciona bien. Pero hay una pequeña limitación cuando las cosas comienzan a volverse más complejas.
Probablemente haya notado que, a diferencia del método anterior, hemos tenido que codificar el formato de hora en la función timeHandler
. ¿Qué sucede cuando queremos pasar información o variables de main()
a un controlador?
Un enfoque ordenado es poner nuestra lógica de controlador en un cierre y cerrar las variables que queremos usar:
El timeHandler
función tiene ahora una sutil papel diferente. En lugar de obligar a la función a un controlador (como hicimos anteriormente), ahora la estamos usando para devolver un controlador. Hay dos elementos clave para hacer que esto funcione.
Primero crea fn
, una función anónima que accede a&guión; o cierra la variable format
formando un cierre. Independientemente de lo que hagamos con el cierre, siempre podrá acceder a las variables locales del ámbito en el que se creó, lo que en este caso significa que siempre tendrá acceso a la variable format
.
en Segundo lugar nuestro cierre tiene la firma func(http.ResponseWriter, *http.Request)
. Como recordarás de antes, esto significa que podemos convertirlo en un tipo HandlerFunc (para que satisfaga la interfaz del Controlador). Nuestra función timeHandler
devuelve este cierre convertido.
En este ejemplo acabamos de pasar una cadena simple a un controlador. Pero en una aplicación del mundo real, podría usar este método para pasar la conexión a la base de datos, el mapa de plantillas o cualquier otro contexto a nivel de aplicación. Es una buena alternativa al uso de variables globales, y tiene el beneficio adicional de crear manejadores autónomos para pruebas.
también puede ver este mismo patrón se escribe como:
O mediante una conversión implícita a la HandlerFunc tipo de retorno:
El DefaultServeMux
Probablemente haya visto DefaultServeMux mencionado en muchos lugares, desde los ejemplos más simples de Hello World hasta el código fuente de Go.
Me llevó mucho tiempo darme cuenta de que no es nada especial. El DefaultServeMux es solo un ServeMux simple como ya hemos estado usando, que se crea una instancia por defecto cuando se usa el paquete HTTP. Aquí está la línea relevante de la fuente Go:
var DefaultServeMux = NewServeMux()
Por lo general, no debe usar DefaultServeMux porque representa un riesgo de seguridad.
Debido a que el DefaultServeMux se almacena en una variable global, cualquier paquete puede acceder a él y registrar una ruta, incluidos los paquetes de terceros que importe su aplicación. Si uno de esos paquetes de terceros está comprometido, podrían usar DefaultServeMux para exponer a un manejador malicioso a la web.
Por lo tanto, como regla general, es una buena idea evitar el servicio predeterminado y, en su lugar, usar su propio ServeMux de ámbito local, como hemos hecho hasta ahora. Pero si decidiste usarlo…
El paquete HTTP proporciona un par de accesos directos para trabajar con DefaultServeMux: http.Handle y http.HandleFunc. Estos hacen exactamente lo mismo que sus funciones homónimas que ya hemos visto, con la diferencia de que agregan controladores al DefaultServeMux en lugar de uno que usted ha creado.
Además, ListenAndServe volverá a usar el servidor predeterminado si no se proporciona otro controlador (es decir, el segundo parámetro se establece en nil
).
Como paso final, vamos a actualizar nuestra aplicación timeHandler para usar el DefaultServeMux en su lugar:
Si te ha gustado esta publicación de blog, no olvides echar un vistazo a mi nuevo libro sobre cómo crear aplicaciones web profesionales con Go!
Sígueme en Twitter @ajmedwards.
Todos los fragmentos de código de esta publicación son de uso gratuito bajo la licencia MIT.