aligned chapter 3 from https://github.com/Jackgris/build-web-application-with-golang_ES
This commit is contained in:
203
es/03.4.md
Normal file
203
es/03.4.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# 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 conexion que creó Conn con el fin de lograr una alta concurrencia y rendimiento, por lo que cada caso 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 puede 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 se habla 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 implementar su interfaz , por eso podríamos agregarlo como controlador ? Debido a que hay otro tipo `HandlerFunc` en el paquete `http` . Llamamos `HandlerFunc` definir nuestra `sayhelloName` , así `sayhelloName` implementa el `Handler` al mismo tiempo. que es como llamamos `HandlerFunc(f)`, y la función `f` es forzado convertido 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 router utiliza la URL como clave para encontrar correspondiente controlador que guarda en un mapa , y llama handler.ServeHTTP para ejecutar funciones para manejar los datos.
|
||||
|
||||
Usted debe entender el flujo de trabajo del router, y Go realmente apoya routers personalizados. El segundo argumento de ListenAndServe es para la configuración del router a medida, que es una interfaz de `Handler`. Por lo tanto , se puede utilizar cualquier router que implemente la interfaz `Handler`.
|
||||
|
||||
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 tu no quieres usar un router, 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 parametriados, 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.
|
||||
|
||||
|
||||
## GO flujo de ejecución del código
|
||||
|
||||
Vamos a echar un vistazo a la lista de flujo de ejecución en conjunto.
|
||||
|
||||
- Se llama http.HandleFunc
|
||||
1. Llame HandleFunc de DefaultServeMux
|
||||
2. Llame Handle de DefaultServeMux
|
||||
3. Agregar reglas del router para mapear [cadena ] muxEntry de DefaultServeMux
|
||||
- Se llama http.ListenAndServe (": 9090 " , nil )
|
||||
1. se instancia el servidor
|
||||
2. Llama ListenAndServe del Servidor
|
||||
3. Llama net.Listen ( " tcp" , addr ) para escuchar en el puerto .
|
||||
4. Iniciar un bucle, y aceptar las solicitudes en el cuerpo del bucle.
|
||||
5. Instanciada una Conn se empieza una goroutine para cada solicitud : ir c.serve ().
|
||||
6. Lee petición de datos : w , err : = c.readRequest ().
|
||||
7. Comprueba si el controlador está vacío, si está vacíoutiliza DefaultServeMux .
|
||||
8. Llama al controlador de ServeHTTP
|
||||
9. Ejecutar 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)
|
||||
Reference in New Issue
Block a user