2017-03-18 3 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 
} 

과 :

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

나는이 두 가지 패턴이 개별적으로 작동 할 수 있습니다,하지만 난 몰라 내가 어떻게 그들을 결합 할 수 있었는지. 나는 내가 앨리스 (Alice)와 같은 도서관으로 미들웨어를 연결할 수 있다는 것을 좋아한다. 그러나 오류를 반환 할 수 있다면 좋을 것입니다. 이것을 달성 할 수있는 방법이 있습니까?

+0

그래서 당신은 오류를 반환하고 싶습니다 ... 어디서? 반환 된 오류를 확인하는 호출자는 누구입니까? – zerkms

답변

0

좋아요이 작업을 수행하는

한 가지 방법은 다음과 같이 매개 변수로 loggingHandler에 전달하는 struct 사용하고 너무 오류를 반환 HandlerFuncs의 패턴, 그것은 깔끔하고 오류 처리기를 한번 작성합니다. 포함 된 핸들러와 별도로 미들웨어를 생각하면 오류를 전달하기 위해 미들웨어가 필요하지 않습니다. 미들웨어는 차례대로 차례대로 실행되는 체인과 같으며, 마지막 미들웨어는 핸들러 서명을 인식하고 오류를 적절히 처리합니다.그것은 간단한 형태의에서

그래서, 당신은 정확히 같은이 미들웨어를 유지하지만, 마지막에이 양식의 (그리고 다른 미들웨어하지만 특별한 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))) 

을하고, 체인의 끝에 와서,하지만 괜찮아요. 또한 좀더 단순하게 만들고 오류 처리기를 피해 가지 않도록 mux에서이 동작을 래핑하는 방법을 고려해보고 라우트 설정에서 추한 배치를하지 않고도보다 쉽게 ​​미들웨어 체인을 만들도록하십시오.

라우터 라이브러리를 사용하는 경우이 패턴이 작동하려면 명시 적으로 지원해야한다고 생각합니다. 당신은 정확하게 당신이 후있어 서명을 사용하는이 라우터에서 변형 된 형태로 행동이의 예를 볼 수 있지만, 미들웨어 체인을 구축하고 수동 포장없이 실행 핸들 :

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

}