-1

This amazing article here: https://www.alexedwards.net/blog/how-to-properly-parse-a-json-request-body explains very well how to write a Golang handler.

I need to use two handlers, one after the other, only if the first one gives error.

Like this:

func main() {
    r := chi.NewRouter()

    r.Post("/api", MyHandlers)
}

func MyHandlers(w http.ResponseWriter, r *http.Request) {

    err := DoSomething(w, r)

    if err != nil {
        println("OMG! Error!")
        DoSomethingWithThisOneInstead(w, r)
    }

}

func DoSomething(w http.ResponseWriter, r *http.Request) error {
    // here I need to read request's Body
    // and I can use io.TeeReader()
    // and I can use all the code in the amazing article example
    // but I don't want to, because it's a lot of code to maintain
    res, err := myLibrary.DoSomething(requestBody)
    if err != nil {
        return err
    }
    render.JSON(w, r, res) // go-chi "render" pkg
    return nil
}

func DoSomethingWithThisOneInstead(w http.ResponseWriter, r *http.Request) {
    // here I need to read request's Body again!
    // and I can use all the code in the amazing article example
    // but I don't want to, because it's a lot of code to maintain
    anotherLibrary.DoSomethingElse.ServeHTTP(w, r)
}
  1. Is there a different method instead of reading twice or more the same request.Body?

  2. Is there a way to avoid writing all that code in the article (which needs to be maintained) and using open source libraries that do it better and are revised by thousands of smarter eyes than mine?

  3. E.G.: Can I use a go-chi method?

Fred Hors
  • 3,258
  • 3
  • 25
  • 71
  • 2
    Is there a requirement that DoSomething and DoSomethingWithThisOneInstead have the type `func(w http.ResponseWriter, r *http.Request)`? If not, then add a `body []byte` argument to the functions. – Charlie Tumahai Jul 02 '20 at 22:17
  • +1 to @MuffinTop. There is no need for `DoSomething` and `DoSomethingWithThisONeInstead` to implement `HandlerFunc` in this example. Also `DoSomething`'s declaration does not return an error but its call site implies that it should return an error. – kingkupps Jul 02 '20 at 22:42
  • @MuffinTop I updated question with more code. As you can see I need `w` and `r` in both handlers. – Fred Hors Jul 02 '20 at 22:49
  • @kingkupps I updated question with more code. As you can see I need `w` and `r` in both handlers. – Fred Hors Jul 02 '20 at 22:49
  • 1
    What about using a TeeReader? – Daniel Jul 02 '20 at 23:06
  • Example of using a `TeeReader` for a very similar problem: https://stackoverflow.com/questions/31884093/read-multiple-time-a-reader – kingkupps Jul 02 '20 at 23:11

1 Answers1

5

Slurp up the bytes and use as needed:

func MyHandlers(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
       // handle error
    }
    r.Body.Close()

    r.Body = ioutil.NopCloser(bytes.NewReader(body))
    err := DoSomething(w, r)

    if err != nil {
        println("OMG! Error!")
        r.Body = ioutil.NopCloser(bytes.NewReader(body))
        DoSomethingWithThisOneInstead(w, r)
    }

}