2012-07-31 8 views
5

Estoy escribiendo un servidor web en el que necesito registrar manejadores en tiempo de ejecución. P.ej. "/ create" crearía un nuevo controlador para todas las URL como "/ 123/*" y así sucesivamente. Necesito un "/ destroy/123" correspondiente que anularía el registro del controlador para "/ 123/*".¿Cómo anulo el registro de un controlador en net/http?

Aquí está el código para manejar "/ crear"

package main 
import (
    "fmt" 
    "net/http" 
) 

type MyHandler struct { 
    id int 
} 
func (hf *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    fmt.Fprintln(w, r.URL.Path) 
} 

// Creates MyHandler instances and registers them as handlers at runtime 
type HandlerFactory struct { 
    handler_id int 
} 
func (hf *HandlerFactory) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    hf.handler_id++ 
    handler := MyHandler{hf.handler_id} 
    handle := fmt.Sprintf("/%d/", hf.handler_id) 
    http.Handle(handle, &handler) 
} 

func main() { 
    factory := HandlerFactory{0} 
    http.Handle("/create", &factory) 
    http.ListenAndServe("localhost:8080", nil) 
} 

intenté implementar mi propio multiplexor mediante la incorporación de http.ServeMux pero que mantiene su asignación patrón a Handler en una variable privada (ServeMux.m)

Respuesta

13

Lo que haría es crear una personalizada ServerMux. Copie el código de GOROOT/src/pkg/net/http/server.go. Se inicia en la línea 837 y finaliza en 939.

El ServerMux personalizado necesitaría un método para anular el registro. Esto debería ser fácil de implementar. Solo toma el candado y del() la entrada del mapa. Por ejemplo (todo el código no probado):

// TODO: check if registered and return error if not. 
// TODO: possibly remove the automatic permanent link between /dir and /dir/. 
func (mux *MyMux) Deregister(pattern string) error { 
    mux.mu.Lock() 
    defer mux.mu.Unlock() 
    del(mux.m, pattern) 
    return nil 
} 

Para utilizar este nuevo mux, deberías hacer algo como esto:

mux := newMux() 
mux.Handle("/create", &factory) 

srv := &http.Server { 
    Addr: localhost:8080 
    Handler: mux, 
} 
srv.ListenAndServe() 

Modificación mux llamando deregister() de otro goroutine es completamente seguro y voluntad modificar la forma en que ListenAndServe() enruta los mensajes.

0

Tal vez la anulación del registro se puede "completar" registrando un controlador que no devuelve nada (no escribiendo nada al ResponseWriter) o generando un tipo de respuesta "no encontrada". Depende de sus requisitos y/o el propósito/efecto de anular el registro de un controlador previamente registrado.

+2

La preocupación es que el registro de controladores seguirá creciendo con el tiempo y causará una desaceleración en el cambio de solicitudes debido a las búsquedas de controladores cada vez más costosas. –

6

Parece que ya ha aceptado una respuesta, pero quería proponer una solución alternativa.

Cuestiono la necesidad de agregar un muxer personalizado. En este ejemplo estoy usando el gorila muxer, sin embargo, eso es solo porque estoy familiarizado con su coincidencia de patrones. En teoría, podría hacer coincidir el patrón de la URL entrante sin la necesidad de reemplazar el muxer predeterminado.

Mi código mantiene las funciones del controlador en un mapa (cadena: el nombre del manejador => literal de la función) ... Esto es adecuado para usar el método muletero HandleFunc predeterminado.

entrada de la muestra/salida:

obtener/registrar/123

GET/123
hello from123.

GET/destruir/123

GET/123
[nothing]

package main 

import (
    "code.google.com/p/gorilla/mux" 
    "flag" 
    "log" 
    "net/http" 
) 

// Wraps server muxer, dynamic map of handlers, and listen port. 
type Server struct { 
    Dispatcher *mux.Router 
    Urls  map[string]func(w http.ResponseWriter, r *http.Request) 
    Port  string 
} 

// May the signal never stop. 
func main() { 
    //Initialize Server 
    server := &Server{Port: "3000", Dispatcher: mux.NewRouter(), Urls: make(map[string]func(w http.ResponseWriter, r *http.Request))} 

    var port = flag.String("port", "3000", "Default: 3000; Set the port for the web-server to accept incoming requests") 
    flag.Parse() 

    server.Port = *port 
    log.Printf("Starting server on port: %s \n", server.Port) 

    server.InitDispatch() 
    log.Printf("Initializing request routes...\n") 

    server.Start() //Launch server; blocks goroutine. 
} 

func (s *Server) Start() { 

    http.ListenAndServe(":"+s.Port, s.Dispatcher) 
} 

// Initialize Dispatcher's routes. 
func (s *Server) InitDispatch() { 
    d := s.Dispatcher 

    // Add handler to server's map. 
    d.HandleFunc("/register/{name}", func(w http.ResponseWriter, r *http.Request) { 
     //somewhere somehow you create the handler to be used; i'll just make an echohandler 
     vars := mux.Vars(r) 
     name := vars["name"] 

     s.AddFunction(w, r, name) 
    }).Methods("GET") 

    d.HandleFunc("/destroy/{name}", func(w http.ResponseWriter, r *http.Request) { 
     vars := mux.Vars(r) 
     name := vars["name"] 
     s.Destroy(name) 
    }).Methods("GET") 

    d.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) { 
     //Lookup handler in map and call it, proxying this writer and request 
     vars := mux.Vars(r) 
     name := vars["name"] 

     s.ProxyCall(w, r, name) 
    }).Methods("GET") 
} 

func (s *Server) Destroy(fName string) { 
    s.Urls[fName] = nil //remove handler 
} 

func (s *Server) ProxyCall(w http.ResponseWriter, r *http.Request, fName string) { 
    if s.Urls[fName] != nil { 
     s.Urls[fName](w, r) //proxy the call 
    } 
} 

func (s *Server) AddFunction(w http.ResponseWriter, r *http.Request, fName string) { 
    f := func(w http.ResponseWriter, r *http.Request) { 
     w.Write([]byte("hello from" + fName)) 
    } 

    s.Urls[fName] = f // Add the handler to our map 
} 
+0

Su solución es bastante buena. Esto es probablemente lo que habría hecho si la interpretación básica de la estructura de URL fuera parte de la biblioteca estándar. Mi requisito actualmente no es lo suficientemente complejo como para traer un nuevo paquete, pero sospecho que no tomará demasiadas iteraciones para que sea así y podría ir por Gorilla en ese momento. –

Cuestiones relacionadas