2

I need to make a http call in golang code. My server code will create the http request object and put it in database.

The worker code will get the data from the DB and should be able to make the http call using the persisted request object.

Can I directly persist the request object in DB by serializing the http request or do I need to add method/URL/body separately in the database?

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
user3565529
  • 1,317
  • 2
  • 14
  • 24
  • 2
    you're making a lot of questions at the same time, looks like you're learning Go, I recommend you taking the [tour](https://tour.golang.org/welcome/1) and/or take a look here: https://gobyexample.com/ – Yandry Pozo Sep 30 '17 at 01:19
  • Try it, and when you get stuck, come back here with a specific question. The answer to your question as currently posed is 'it depends'. – Kenny Grant Sep 30 '17 at 08:30
  • 1
    https://golang.org/pkg/net/http/#Request.Write – Peter Oct 02 '17 at 14:05

2 Answers2

5

There's not a nice way to serialize the http.Request structure itself (see notes below), but you can serialize the request back into HTTP/1.1 wire format (and deserialize too). This can be done using Request.Write and http.ReadRequest:

func captureRequestData(req *http.Request) error {
    var b = &bytes.Buffer{} // holds serialized representation
    var tmp *http.Request
    var err error
    if err = req.Write(b); err != nil { // serialize request to HTTP/1.1 wire format
        return err
    }
    r := bufio.NewReader(b)
    if tmp, err = http.ReadRequest(r); err != nil { // deserialize request
        return err
    }
    *req = *tmp // replace original request structure
    return nil
}

Persistence of the serialized buffer is just a matter of saving a copy of b. You might prefer to persist the string representation, which involves some additional conversions:

s := b.String() // persist this
r := bufio.NewReader(strings.NewReader(s))

There are a couple of reasons, at least, why you can't just use json.Marshal() for this:

  1. The public fields of Request include functions, which Marshall does not accept:

    json: unsupported type: func() (io.ReadCloser, error)

  2. Request also contains a private field ctx which thwarts any generic attempts at deserialization:

    ctx is either the client or server context. It should only be modified via copying the whole Request using WithContext. It is unexported to prevent people from using Context wrong and mutating the contexts held by callers of the same request.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • BTW: `httputil.DumpRequest` and `Request.Write` likely have the same implementation under the hood -- so either could work for this, but the [`DumpRequest` documentation](https://golang.org/pkg/net/http/httputil/#DumpRequest) says that it should only be used for debugging purposes. – Brent Bradburn Nov 26 '19 at 22:05
  • Serializing the [`Response`](https://golang.org/pkg/net/http/#Response) can be done using a nearly identical routine, with a small amount of substitution (`req`/`resp`, etc.). [`http.ReadResponse`](https://golang.org/pkg/net/http/#ReadResponse) takes one additional argument, which can be `nil` (it doesn't seem to matter in my testing). – Brent Bradburn Nov 26 '19 at 22:29
  • One liner to dump a complete request for debugging (warning: security issues): `fmt.Println("Request: ", string(assumeNoError(httputil.DumpRequest(request /*body*/, true)).([]byte)))`. Requires: [assumeNoError](https://stackoverflow.com/a/64346779/86967) – Brent Bradburn Jul 05 '22 at 20:38
  • Related/opposite question: [Parse HTTP requests and responses from text file in Go](https://stackoverflow.com/q/33963467/86967) – Brent Bradburn Nov 13 '22 at 01:10
0

Yes you can: https://golang.org/pkg/net/http/httputil/#DumpRequest

import "net/http/httputil"

dumped, err := httputil.DumpRequest(req, true)