2017-03-18 14 views
3

私はこのようなゴー・ミドルウェア・パターンを熟知しています:Goミドルウェアパターンとリクエストハンドラを返すエラーをどのように組み合わせることができますか?

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

だから、例えば私がloggingHandlerを持っていた場合:

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

とシンプルhandleFunc:

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

私は可能性があり次のように組み合わせてください:

http.Handle("/", loggingHandler(http.HandlerFunc(handleFunc))) 
log.Fatal(http.ListenAndServe(":8080", nil)) 

これはすべて問題ありません。

しかし、私はハンドラが通常の機能のようにエラーを返すという考えが好きです。これは、エラーがあった場合にエラーを返すことができるだけでなく、関数の最後にnilを返すので、エラー処理がずっと簡単になります。

私はこのようにそれを行っている:

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 
} 

そして、このようにそれをラップすることにより、それを使用する:私は、これら2つのパターンが個別に動作しますが、私は知らないことができます

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

どのように私はそれらを組み合わせることができます。私はミドルウェアをアリスのような図書館と連鎖させることができるのが好きです。しかし、エラーを返すことができればいいと思う。これを達成する方法はありますか?

+0

だから、エラーを返そうとしています...どこですか?返されたエラーをチェックする呼び出し元は誰ですか? – zerkms

答えて

0

私はこれが好きでこれを行うには

一つの方法は、このようなパラメータとして、あなたのloggingHandlerに渡しstructを使用していますHandlerFuncsのパターンもエラーを返すので、はるかにきれいで、エラーハンドラを一度書くだけです。ミドルウェアはハンドラとは別に考えるだけでミドルウェアにエラーを渡す必要はありません。ミドルウェアはチェーンごとに順番に実行され、最後のミドルウェアはハンドラのシグネチャを認識し、エラーを適切に処理します。

だからそれであなたが正確に同じを持っているミドルウェアを維持し、最も単純な形だが、この形式であるエンドインサート1(および他のミドルウェアが、特別な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 
     } 
    }) 
} 

...

次に、このようなあなたの機能ラップ:しかあなたの特別な関数のシグネチャをラップすることができ、この特別なエラーミドルウェアを意味

moreMiddleware(myMiddleWare(errorHandler(myhandleFuncReturningError))) 

をし、チェーンの終わりに来る、しかしそれはいいです。また、私はあなた自身のマルチプレクサでこの動作をラップして、エラーハンドラを回避し、ルート設定で醜いラッピングをせずにミドルウェアのチェーンをより簡単に構築できるようにすることを検討したいと思います。

ルータライブラリを使用している場合は、おそらくこのパターンがうまく動作するよう明示的にサポートする必要があります。あなたは後にしている正確に署名を使用しますが、ミドルウェア・チェーンを構築し、手動ラッピングせずにそれを実行扱うこのルータで修正された形でこの動作の例を見ることができます:

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

+0

助けてくれてありがとう! – Alex

0

ミドルウェアの出力は、定義上、HTTP応答です。エラーが発生した場合、要求が実行されないようにするか、ミドルウェアがHTTPエラー(サーバー上で予期せず何か問題が発生した場合は500)を返します。そうでない場合は、それをシステム管理者が修正することができ、実行を続行する必要があります。

あなたはこのような状況をキャッチして、サーバーをクラッシュすることなく、後でそれを処理し、(私は意図的にこれを行うことはお勧めしませんが)あなたの関数がパニックすることを可能にすることによって、これを実現したい場合は、例は、セクションパニック回復にthis blog postにあり(Aliceも使用しています)

0

あなたのerrorHandler機能をチェーンして、あなたのloggingHandlerに組み合わせたいと思っていたからです。

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 
} 

mainあなたはこのようにそれを呼び出すことで:

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

} 
関連する問題