Skip to content

Latest commit

 

History

History
203 lines (173 loc) · 8.71 KB

File metadata and controls

203 lines (173 loc) · 8.71 KB

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