16

実行時にハンドラーを登録する必要があるWebサーバーを作成しています。たとえば、「/ create」は、「/ 123/*」などのすべてのURLに対して新しいハンドラーを作成します。「/123/*」のハンドラーの登録を解除する、対応する「/ destroy/123」が必要です。

「/create」を処理するためのコードは次のとおりです

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)
}

埋め込みによって独自のマルチプレクサを実装しようとhttp.ServeMuxしましたが、パターンからハンドラへのマッピングがプライベート変数に保持されています(ServeMux.m

4

3 に答える 3

15

私がすることは、カスタムを作成することですServerMux。からコードをコピーしますGOROOT/src/pkg/net/http/server.go。837 行目から始まり、939 行目で終わります。

カスタム ServerMux には、登録解除のメソッドが必要です。これは簡単に実装できるはずです。del()ロックとマップエントリをつかむだけです。例(すべてのコードはテストされていません):

// 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
}

この新しいマルチプレクサを使用するには、次のようにします。

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

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

deregister()別の goroutine から呼び出して mux を変更することは完全に安全であり、ListenAndServe()メッセージのルーティング方法を変更します。

于 2012-07-31T15:35:52.710 に答える
8

すでに回答を受け入れているようですが、別の解決策を提案したかったのです。

カスタム マルチプレクサを追加する必要性について疑問があります。この例では、ゴリラ マルチプレクサを使用していますが、それは単にパターン マッチングに精通しているからです。理論的には、デフォルトのマルチプレクサを置き換えることなく、着信 URL からパターンを照合できます。

私のコードは、マップ内のハンドラー関数を維持します (文字列: ハンドラー名 => 関数リテラル)... これは、既定のマルチプレクサの HandleFunc メソッドを使用するのに適しています。

入出力例:

GET /登録/123

GET /123
hello from123.

/destroy/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
}
于 2012-08-07T18:33:44.223 に答える
0

おそらく、登録解除は、何も返さない (ResponseWriter に何も書き込まない) ハンドラーを登録するか、「見つからない」種類の応答を生成することによって「実行」できます。要件および/または以前に登録されたハンドラーの登録解除の目的/効果によって異なります。

于 2012-07-31T10:57:50.500 に答える