robert-min / handson-go

Go-lang hands on guide
0 stars 0 forks source link

Chapter6. HTTP Server develop - Object Sharing(custom handler), Server Middleware(handler Function) #17

Open robert-min opened 1 year ago

robert-min commented 1 year ago

핸들러 함수 간에 객체(데이터 공유)

robert-min commented 1 year ago

핸들러 함수 간에 객체 공유 코드


type appConfig struct {
    logger *log.Logger
}

type app struct {
    config  appConfig
    handler func(w http.ResponseWriter, r *http.Request, confing appConfig)
}

// 공통된 객체를 공유하는 핸들러 생성
func (a app) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    a.handler(w, r, a.config)
}

func apiHandler(w http.ResponseWriter, r *http.Request, config appConfig) {
    config.logger.Println("Handling API request")
    fmt.Fprintln(w, "Hello, world!")
}

func healthCheckHandler(w http.ResponseWriter, r *http.Request, config appConfig) {
    if r.Method != "GET" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    config.logger.Println("Handling healthcheck request")
    fmt.Fprintf(w, "ok")
}

func setupHandler(mux *http.ServeMux, config appConfig) {
    mux.Handle("/healthz", &app{config: config, handler: healthCheckHandler})
    mux.Handle("/api", &app{config: config, handler: apiHandler})
}

func main() {
    listenAddr := os.Getenv("LISTENADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    config := appConfig{
        logger: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
    }

    mux := http.NewServeMux()
    setupHandler(mux, config)

    log.Fatal(http.ListenAndServe(listenAddr, mux))
}
robert-min commented 1 year ago

Server Middleware

robert-min commented 1 year ago

Server Middleware 코드


type appConfig struct {
    logger *log.Logger
}

type app struct {
    config  appConfig
    handler func(w http.ResponseWriter, r *http.Request, config appConfig)
}

func (a app) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    a.handler(w, r, a.config)
}

func healthCheckHandler(w http.ResponseWriter, r *http.Request, config appConfig) {
    if r.Method != "GET" {
        config.logger.Printf("error=\"Invalid request\" path=%s method=%s", r.URL.Path, r.Method)
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    fmt.Fprintf(w, "ok")
}

func apiHandler(w http.ResponseWriter, r *http.Request, config appConfig) {
    fmt.Fprintf(w, "Hello, World")
}

func panicHandler(w http.ResponseWriter, r *http.Request, config appConfig) {
    panic("I panicked")
}

func setupHandlers(mux *http.ServeMux, config appConfig) {
    mux.Handle("/healthz", &app{config: config, handler: healthCheckHandler})
    mux.Handle("/api", &app{config: config, handler: apiHandler})
    mux.Handle("/panic", &app{config: config, handler: panicHandler})
}

func loggingMiddleware(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        startTime := time.Now()
        h.ServeHTTP(w, r)
        log.Printf("protocol=%s path=%s method=%s duration=%f", r.Proto, r.URL.Path, r.Method, time.Now().Sub(startTime).Seconds())
    })
}

func panicMiddleware(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if rValue := recover(); rValue != nil {
                log.Println("panic detected when handling request", rValue)
                w.WriteHeader(http.StatusInternalServerError)
                fmt.Fprintf(w, "Unexpected server error occured")
            }
        }()
        h.ServeHTTP(w, r)
    })
}

func main() {
    listenAddr := os.Getenv("LISTEN_ADDR")
    if len(listenAddr) == 0 {
        listenAddr = ":8080"
    }

    config := appConfig{
        logger: log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
    }
    mux := http.NewServeMux()
    setupHandlers(mux, config)

    m := loggingMiddleware(panicMiddleware(mux))

    log.Fatal(http.ListenAndServe(listenAddr, m))
}