2017-03-18 3 views
3

Ich bin vertraut mit dem Go-Middleware-Muster wie folgt aus:Wie kann ich das Go-Middleware-Muster mit Fehleranfragehandlern kombinieren?

// Pattern for writing HTTP middleware. 
func middlewareHandler(next http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // Our middleware logic goes here before executing application handler. 
     next.ServeHTTP(w, r) 
     // Our middleware logic goes here after executing application handler. 
    }) 
} 

So zum Beispiel, wenn ich eine loggingHandler hatte:

func loggingHandler(next http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // Before executing the handler. 
     start := time.Now() 
     log.Printf("Strated %s %s", r.Method, r.URL.Path) 
     next.ServeHTTP(w, r) 
     // After executing the handler. 
     log.Printf("Completed %s in %v", r.URL.Path, time.Since(start)) 
    }) 
} 

Und ein einfaches handleFunc:

func handleFunc(w http.ResponseWriter, r *http.Request) { 
    w.Write([]byte(`Hello World!`)) 
} 

ich konnte Kombiniere sie wie folgt:

Das ist alles in Ordnung.

Aber ich mag die Idee von Handlern, die Fehler wie normale Funktionen zurückgeben können. Dies erleichtert die Fehlerbehandlung erheblich, da ich einfach einen Fehler zurückgeben kann, wenn ein Fehler vorliegt, oder einfach nil am Ende der Funktion zurückgeben.

Ich habe es so gemacht:

type errorHandler func(http.ResponseWriter, *http.Request) error 

func (f errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    err := f(w, r) 
    if err != nil { 
     // log.Println(err) 
     fmt.Println(err) 
     os.Exit(1) 
    } 
} 

func errorHandle(w http.ResponseWriter, r *http.Request) error { 
    w.Write([]byte(`Hello World from errorHandle!`)) 
    return nil 
} 

Und es dann verwenden, indem Sie es wie folgt Einwickeln:

http.Handle("/", errorHandler(errorHandle)) 

kann ich diese beiden Muster machen separat arbeiten, aber ich weiß nicht, wie ich sie kombinieren könnte. Ich mag es, dass ich Middlewares mit einer Bibliothek wie Alice ketten kann. Aber es wäre schön, wenn sie auch Fehler zurückgeben könnten. Gibt es einen Weg, dies zu erreichen?

+0

So Sie will die fehler zurückgeben ... wo? Wer wäre der Anrufer, der den zurückgegebenen Fehler überprüfen würde? – zerkms

Antwort

0

Ich mag diese Muster von HandlerFuncs gibt auch Fehler zurück, es ist viel besser und Sie schreiben Ihren Fehlerhandler einmal. Denken Sie nur an Ihre Middleware getrennt von den Handlern, die sie enthält, Sie brauchen die Middleware nicht, um Fehler zu übergeben. Die Middleware ist wie eine Kette, die jede der Reihe nach ausführt, und dann ist die allerletzte Middleware diejenige, die Ihre Handler-Signatur kennt und sich mit dem Fehler entsprechend befasst.

So in der einfachsten Form, halten Sie die Middleware Sie genau das gleiche, aber am Ende ein einzufügen, die von dieser Form ist (und führen Sie keine weitere Middleware, sondern ein spezielles HandlerFunc):

// Use this special type for your handler funcs 
type MyHandlerFunc func(w http.ResponseWriter, r *http.Request) error 


// Pattern for endpoint on middleware chain, not takes a diff signature. 
func errorHandler(h MyHandlerFunc) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // Execute the final handler, and deal with errors 
     err := h(w, r) 
     if err != nil { 
      // Deal with error here, show user error template, log etc 
     } 
    }) 
} 

...

dann Ihre Funktion wie diese wickeln:

moreMiddleware(myMiddleWare(errorHandler(myhandleFuncReturningError))) 

, dass diese spezielle Fehlermiddleware bedeutet, können Sie Ihre spezielle Funktion Unterschrift immer nur wickeln, und komm am Ende der Kette, aber das ist in Ordnung. Außerdem würde ich dieses Verhalten in einen eigenen Mux einbinden, um es ein wenig einfacher zu machen und zu vermeiden, Fehlerhandler herumzugeben, und Sie können eine Kette von Middleware einfacher erstellen, ohne hässliches Wrapping in Ihrer Routeneinrichtung zu haben.

Ich denke, wenn Sie eine Router-Bibliothek verwenden, benötigt es explizite Unterstützung für dieses Muster wahrscheinlich funktionieren. Sie können ein Beispiel dafür in Aktion in einer modifizierten Form in diesem Router sehen, was genau verwendet die Signaturen Sie nach, aber Griffe eine Middleware-Kette zu bauen und es ohne manuelle Verpackung Ausführung:

https://github.com/fragmenta/mux/blob/master/mux.go

+0

Danke für die Hilfe! – Alex

0

Die Ausgabe einer Middleware ist per Definition eine HTTP-Antwort. Wenn ein Fehler aufgetreten ist, wird entweder verhindert, dass die Anforderung erfüllt wird. In diesem Fall sollte die Middleware einen HTTP-Fehler zurückgeben (500, wenn auf dem Server unerwartet etwas schief gegangen ist) oder nicht. In diesem Fall sollte alles protokolliert werden dass es von einem Systemadministrator behoben werden kann, und die Ausführung sollte fortgesetzt werden.

Wenn Sie dies erreichen wollen, indem Sie Ihre Funktionen zur Panik ermöglicht diese Situation (obwohl ich würde nicht empfehlen, dies absichtlich tun), Auffangen und Behandlung später, ohne den Server abstürzt, gibt es ein Beispiel in this blog post in Abschnitt Panik Rettung (es verwendet sogar Alice).

0

Von dem, was ich verstehe, wollten Sie Ihre errorHandler Funktion verketten und und sie in Ihrem loggingHandler kombinieren.

Eine Möglichkeit, dies zu tun, ist ein struct vorbei, um sie Ihren loggingHandler als Parameter wie folgt aus:

func loggingHandler(errorHandler ErrorHandler, next http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // Call your error handler to do thing 
     err := errorHandler.ServeHTTP() 
     if err != nil { 
      log.Panic(err) 
     } 

     // next you can do what you want if error is nil. 
     log.Printf("Strated %s %s", r.Method, r.URL.Path) 
     next.ServeHTTP(w, r) 
     // After executing the handler. 
     log.Printf("Completed %s in %v", r.URL.Path, time.Since(start)) 
    }) 
} 

// create the struct that has error handler 
type ErrorHandler struct { 
} 

// I return nil for the sake of example. 
func (e ErrorHandler) ServeHTTP() error { 
    return nil 
} 

und in der main Sie es so nennen:

func main() { 
    port := "8080" 
    // you can pass any field to the struct. right now it is empty. 
    errorHandler := ErrorHandler{} 

    // and pass the struct to your loggingHandler. 
    http.Handle("/", loggingHandler(errorHandler, http.HandlerFunc(index))) 


    log.Println("App started on port = ", port) 
    err := http.ListenAndServe(":"+port, nil) 
    if err != nil { 
     log.Panic("App Failed to start on = ", port, " Error : ", err.Error()) 
    } 

} 
Verwandte Themen