在Go语言中将表单值附加到GET/POST请求中

huangapple go评论118阅读模式
英文:

Appending form value to GET/POST Requests in Go

问题

我想定义一个http.Client,它可以自动将一个表单值附加到所有的GET/POST请求中。

我尝试实现http.RoundTripper,从另一个库中复制/粘贴了这个技术来修改每个请求的头部。

  1. type Transport struct {
  2. // Transport is the HTTP transport to use when making requests.
  3. // It will default to http.DefaultTransport if nil.
  4. // (It should never be an oauth.Transport.)
  5. Transport http.RoundTripper
  6. }
  7. // Client returns an *http.Client that makes OAuth-authenticated requests.
  8. func (t *Transport) Client() *http.Client {
  9. return &http.Client{Transport: t}
  10. }
  11. func (t *Transport) transport() http.RoundTripper {
  12. if t.Transport != nil {
  13. return t.Transport
  14. }
  15. return http.DefaultTransport
  16. }
  17. func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
  18. // To set the Authorization header, we must make a copy of the Request
  19. // so that we don't modify the Request we were given.
  20. // This is required by the specification of http.RoundTripper.
  21. req = cloneRequest(req)
  22. req.Form.Set("foo", bar)
  23. // Make the HTTP request.
  24. return t.transport().RoundTrip(req)
  25. }
  26. // cloneRequest returns a clone of the provided *http.Request.
  27. // The clone is a shallow copy of the struct and its Header map.
  28. func cloneRequest(r *http.Request) *http.Request {
  29. // shallow copy of the struct
  30. r2 := new(http.Request)
  31. *r2 = *r
  32. // deep copy of the Header
  33. r2.Header = make(http.Header)
  34. for k, s := range r.Header {
  35. r2.Header[k] = s
  36. }
  37. return r2
  38. }

然而,这并不起作用。在这个阶段,req.Form值映射似乎不存在,所以我得到了恐慌错误:
panic: runtime error: assignment to entry in nil map

我尝试在(t *Transport) RoundTrip中添加以下代码,但没有成功:

  1. err := req.ParseForm()
  2. misc.PanicIf(err)

我不知道我在做什么,有什么提示吗?

编辑:在cloneRequest方法中尝试复制req.Form值是没有意义的,因为r.Form是一个空映射。

英文:

I would like to define a http.Client that automatically appends a form value to all GET/POST requests.

I naively tried implementing http.RoundTripper as copy/pasted from another library uses this technique to modify headers for every request.

  1. type Transport struct {
  2. // Transport is the HTTP transport to use when making requests.
  3. // It will default to http.DefaultTransport if nil.
  4. // (It should never be an oauth.Transport.)
  5. Transport http.RoundTripper
  6. }
  7. // Client returns an *http.Client that makes OAuth-authenticated requests.
  8. func (t *Transport) Client() *http.Client {
  9. return &http.Client{Transport: t}
  10. }
  11. func (t *Transport) transport() http.RoundTripper {
  12. if t.Transport != nil {
  13. return t.Transport
  14. }
  15. return http.DefaultTransport
  16. }
  17. func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
  18. // To set the Authorization header, we must make a copy of the Request
  19. // so that we don't modify the Request we were given.
  20. // This is required by the specification of http.RoundTripper.
  21. req = cloneRequest(req)
  22. >> req.Form.Set("foo", bar)
  23. // Make the HTTP request.
  24. return t.transport().RoundTrip(req)
  25. }
  26. // cloneRequest returns a clone of the provided *http.Request.
  27. // The clone is a shallow copy of the struct and its Header map.
  28. func cloneRequest(r *http.Request) *http.Request {
  29. // shallow copy of the struct
  30. r2 := new(http.Request)
  31. *r2 = *r
  32. // deep copy of the Header
  33. r2.Header = make(http.Header)
  34. for k, s := range r.Header {
  35. r2.Header[k] = s
  36. }
  37. return r2
  38. }

This however does not work. The req.Form values map doesn't seem to exist at this stage, so I get the panic:
panic: runtime error: assignment to entry in nil map

I tried adding this to the (t *Transport) RoundTrip, but no luck:

  1. err := req.ParseForm()
  2. misc.PanicIf(err)

I've no idea what I'm doing, any tips?

EDIT: There is no point in trying to copy req.Form values in cloneRequest method, since r.Form is empty map anyway.

答案1

得分: 8

FormPostFormParseForm()只在接收请求时使用。在发送请求时,传输层期望数据已经被正确编码。

你的想法是正确的,通过包装RoundTrip来实现,但你必须自己处理编码后的数据。

  1. if req.URL.RawQuery == "" {
  2. req.URL.RawQuery = "foo=bar"
  3. } else {
  4. req.URL.RawQuery = req.URL.RawQuery + "&" + "foo=bar"
  5. }

或者可以选择以下方式:

  1. form, _ = url.ParseQuery(req.URL.RawQuery)
  2. form.Add("boo", "far")
  3. req.URL.RawQuery = form.Encode()

如果你想避免重复的键,也可以事先检查url.Values。另外,检查Content-Type头部是否为multipart/form-dataapplication/x-www-form-urlencoded,以避免与其他类型的查询产生冲突,也是一个不错的主意。

英文:

Form, PostForm, and ParseForm() are only used when receiving a request. When sending a request, the Transport expects the data to be properly encoded.

You have the right idea by wrapping RoundTrip, but you have to handle the encoded data yourself.

  1. if req.URL.RawQuery == "" {
  2. req.URL.RawQuery = "foo=bar"
  3. } else {
  4. req.URL.RawQuery = req.URL.RawQuery + "&" + "foo=bar"
  5. }

or alternatively:

  1. form, _ = url.ParseQuery(req.URL.RawQuery)
  2. form.Add("boo", "far")
  3. req.URL.RawQuery = form.Encode()

You could also choose to check the url.Values beforehand, if you want to avoid duplicating keys. Checking the Content-Type header for multipart/form-data or application/x-www-form-urlencoded to avoid interaction with other types of queries may be a good idea too.

huangapple
  • 本文由 发表于 2014年12月5日 02:13:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/27301051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定