1

What’s the equivalent to middleware handlers in Google Cloud Functions?

In standard approach, normally I do:

router.Handle("/receive", middlewares.ErrorHandler(MyReceiveHandler))

And then, in the middleware:

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

func (fn ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    err := fn(w, r) 
    if err == nil {
        return
    }
    log.Printf("An error accured: %v", err) 
    clientError, ok := err.(errors.BaseError) 
    if !ok {
        w.WriteHeader(500) 
        return
    }

    w.WriteHeader(clientError.GetStatusCode())
    w.Write([]byte(clientError.Error()))
}

In AWS Lambda, I can achieve the same thing using, for example:

func main() {
    lambda.Start(
        middlewares.Authentication(Handler),
    )
}

But I could not find a way to do this in GCP Functions. How would it work?

Can you help me?

thiago
  • 377
  • 5
  • 20

1 Answers1

1

Let's say you start with the following server code in your development environment:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.Handle("/", MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)))
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal(err)
    }
}

func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        fmt.Fprint(w, msg)
    })
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprint(w, "Hello, World!")
}

As far as I can tell, GCF requires its entry point to be an exported identifier of type func(http.ResponseWriter, *http.Request) (not http.HandlerFunc, not http.Handler); therefore, if you have a http.Handler, you'll need to select its ServeHTTP method explicitly to obtain a function of the expected type. However, that identifier can be a package-level function, a method, or a variable.

Here is how you can adapt the code above for GCF:

package p

import (
    "fmt"
    "net/http"
)

// use F as your GCF's entry point
var F = MiddlewareFinalMsg(" Goodbye!", http.HandlerFunc(HelloWorld)).ServeHTTP

func MiddlewareFinalMsg(msg string, h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        fmt.Fprint(w, msg)
    })
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprint(w, "Hello, World!")
}
jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • I don't think a got it.. could you show me a code example, please? sorry – thiago Jul 12 '21 at 22:51
  • Now I can understand clearly, though my function returns an error, since it is an error handling middleware (type ErrorHandler func(http.ResponseWriter, *http.Request) error). Would it still be possible? – thiago Jul 13 '21 at 12:56
  • 1
    @thiago In your code snippet, what is the signature of `router.Handle`? Also, I fail to see what makes ErrorHandler a middleware. It doesn't take a handler and return a new handler... – jub0bs Jul 13 '21 at 13:04