# 3.4 Obteniendo el paquete http En las secciones anteriores, hemos aprendido sobre el flujo de trabajo de la web, y hablamos un poco sobre el paquete `http`. En esta sección, vamos a aprender dos funciones básicas que estan en el paquete `http`: Conn, ServeMux. ## goroutine en Conn A diferencia de los servidores HTTP normales, Go utiliza goroutine para toda trabajo inicializado por Conn con el fin de lograr una alta concurrencia y rendimiento, por lo que cada trabajo es independiente. Go usa el siguiente código para esperar a nuevas conexiones de clientes . ``` c, err := srv.newConn(rw) if err != nil { continue } go c.serve() ``` Como puedes ver, se crea una goroutine para cada conexión , y se pasa el controlador que es capaz de leer los datos de solicitud a la goroutine. ## ServeMux personalizado Utilizamos el enrutamiento por defecto en la sección anterior, cuando hablamos de conn.server, el router pasa los datos de solicitud como back-end al controlador. El struct del router por defecto: ``` type ServeMux struct { mu sync.RWMutex //debido a la concurrencia, tenemos que utilizar mutex aquí m map[string]muxEntry //routers, cada asignación de cadena a un controlador } ``` El struct de muxEntry: ``` type muxEntry struct { explicit bool // exact match or not h Handler } ``` La interfaz de Handler: ``` type Handler interface { ServeHTTP(ResponseWriter, *Request) // routing implementer } ``` `Handler` es una interfaz, pero la función `sayhelloName` no implementa su interfaz, entonces ¿cómo podríamos agregarla como controlador? Debido a que hay otro tipo `HandlerFunc` en el paquete `http`. Nosotros llamamos `HandlerFunc` para definir nuestro método `sayhelloName` , así `sayhelloName` implementa el `Handler` al mismo tiempo. Es como si llamaramos `HandlerFunc(f)`, y la función `f` es forzado a convertirce al tipo `HandlerFunc`. ``` type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } ``` ¿Cómo enrutador llama los controladores después de establecer reglas del router? El enrutador llama `mux.handler.ServeHTTP(w , r)` cuando recibe solicitudes. En otras palabras, se llama la interfaz `ServeHTTP` de los controladores. Ahora, vamos a ver cómo funciona `mux.handler`. ``` func (mux *ServeMux) handler(r *Request) Handler { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones h := mux.match(r.Host + r.URL.Path) if h == nil { h = mux.match(r.URL.Path) } if h == nil { h = NotFoundHandler() } return h } ``` El enrutador utiliza la URL como llave para encontrar el controlador correspondiente que guarda en un mapa, luego llama handler.ServeHTTP para ejecutar funciones y manejar los datos. En este punto, debes entender el flujo de trabajo del enrutador, y Go realmente apoya routers personalizados. El segundo argumento de ListenAndServe es para la configuración del enrutadores a la medida, entonces cualquier enrutador que implemente la interfaz de `Handler` puede ser utilizado. El siguiente ejemplo muestra cómo implementar un enrutador sencillo. ``` package main import ( "fmt" "net/http" ) type MyMux struct { } func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { sayhelloName(w, r) return } http.NotFound(w, r) return } func sayhelloName(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello myroute!") } func main() { mux := &MyMux{} http.ListenAndServe(":9090", mux) } ``` # Enrutamiento Si no quieres usar un enrutador, todavía puedes lograr lo que escribimos en la sección de arriba reemplazando el segundo argumento de `ListenAndServe` a nil, y registrando las URLS usando un `HandleFunc` función que recorre todas las URLs registradas para encontrar la mejor coincidencia, entonces debemos preocuparnos por el orden de registro. código de ejemplo: ``` http.HandleFunc("/", views.ShowAllTasksFunc) http.HandleFunc("/complete/", views.CompleteTaskFunc) http.HandleFunc("/delete/", views.DeleteTaskFunc) //ShowAllTasksFunc es usado para manejar la URL "/" que tiene por defecto todo //TODO agregar manejador para 404 func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { context := db.GetTasks("pending") //true cuando tu no quieres eliminar tareas //db es un paquete que interactua con la base de datos if message != "" { context.Message = message } homeTemplate.Execute(w, context) message = "" } else { message = "Method not allowed" http.Redirect(w, r, "/", http.StatusFound) } } ``` Esto está bien para aplicaciones simples las cuales no necesitan ruteos parametrizados, pero ¿cuándo necesitas eso? Puedes usar las herramientas o frameworks, pero como este libro está enfocado en crear aplicaciones web en Go, vamos a enseñarte como manejar ese escenario también. Cuando la concidencia es hecha, se llama a la función definida en `HandleFunc`, así que supongamos que estamos escribiendo un manejador para una lista y queremos eliminar una tarea, así que la aplicación decide que función se va a llamar cuando llegue la petición `/delete/1`, entonces registramos la URL de la siguiente manera: `http.HandleFunc("/delete/", views.DeleteTaskFunc)` `/delete/1` this URL matches closest with the "/delete/" URL than any other URL so in the `r.URL.path` we get the entire URL of the request. ``` http.HandleFunc("/delete/", views.DeleteTaskFunc) //DeleteTaskFunc is used to delete a task, trash = move to recycle bin, delete = permanent delete func DeleteTaskFunc(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { id := r.URL.Path[len("/delete/"):] if id == "all" { db.DeleteAll() http.Redirect(w, r, "/", http.StatusFound) } else { id, err := strconv.Atoi(id) if err != nil { fmt.Println(err) } else { err = db.DeleteTask(id) if err != nil { message = "Error deleting task" } else { message = "Task deleted" } http.Redirect(w, r, "/", http.StatusFound) } } } else { message = "Method not allowed" http.Redirect(w, r, "/", http.StatusFound) } } ``` Enlace: https://github.com/thewhitetulip/Tasks/blob/master/views/views.go#L170-#L195 Este método lo que hace básicamente es que la función que maneja la URL `/delete/`, se toma la URL completa, que es `/delete/1`, se toman segmentos de la cadena y se extraen todo lo que después de la cadena de coincidencia, en este caso es `1`. Entonces usamos el paquete `strconv` para convertir la cadena en entero y eliminar la tarea con ese identificador. En escenarios mas complejos también podemos utilizar este método, la ventaja es que no vamos a tener que usar herramientas de terceros, pero las herramientas de terceros tienden a ser útiles en su sentido propio. Tienes que tomar la decisión de cuál método prefieres. Ninguna respuesta es la respuesta correcta. ## Flujo de ejecución del código en GO Vamos a echar un vistazo a la lista de flujo de ejecución en conjunto. - Se llama `http.HandleFunc` 1. Se Llama HandleFunc de DefaultServeMux 2. Se Llama Handle de DefaultServeMux 3. Se agregan las reglas del enrutamiento a map[string]muxEntry de DefaultServeMux - Se llama `http.ListenAndServe(":9090" , nil)` 1. Se instancia el servidor 2. Se Llama ListenAndServe del Servidor 3. Se Llama net.Listen ( "tcp" , addr ) para escuchar en el puerto. 4. Iniciar un bucle, y aceptar las solicitudes en el cuerpo del bucle. 5. Instanciar una Conn y se empieza una goroutine para cada solicitud : `go c.serve ()` 6. Se Lee petición de datos : `w , err : = c.readRequest ()` 7. Se Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux . 8. Se Llama al controlador de ServeHTTP 9. Se Ejecuta el código en DefaultServeMux en este caso. 10. Elije el controlador URL y ejecutar código del controlador en esta seccion: `mux.handler.ServeHTTP (w , r)` 11. Cómo elegir handler: A. Normas de router de verificación para esta URL. B. Llamar ServeHTTP en ese controlador, si es que existe. C. Llamar ServeHTTP de NotFoundHandler lo contrario. ## Enlaces - [Indice](preface.md) - Sección anterior: [Como trabaja Go con la web](03.3.md) - Siguiente sección: [Resumen](03.5.md)