英文:
Appending form value to GET/POST Requests in Go
问题
我想定义一个http.Client
,它可以自动将一个表单值附加到所有的GET/POST请求中。
我尝试实现http.RoundTripper
,从另一个库中复制/粘贴了这个技术来修改每个请求的头部。
type Transport struct {
// Transport is the HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
// (It should never be an oauth.Transport.)
Transport http.RoundTripper
}
// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *Transport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
// To set the Authorization header, we must make a copy of the Request
// so that we don't modify the Request we were given.
// This is required by the specification of http.RoundTripper.
req = cloneRequest(req)
req.Form.Set("foo", bar)
// Make the HTTP request.
return t.transport().RoundTrip(req)
}
// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
*r2 = *r
// deep copy of the Header
r2.Header = make(http.Header)
for k, s := range r.Header {
r2.Header[k] = s
}
return r2
}
然而,这并不起作用。在这个阶段,req.Form
值映射似乎不存在,所以我得到了恐慌错误:
panic: runtime error: assignment to entry in nil map
我尝试在(t *Transport) RoundTrip
中添加以下代码,但没有成功:
err := req.ParseForm()
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.
type Transport struct {
// Transport is the HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
// (It should never be an oauth.Transport.)
Transport http.RoundTripper
}
// Client returns an *http.Client that makes OAuth-authenticated requests.
func (t *Transport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *Transport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
// To set the Authorization header, we must make a copy of the Request
// so that we don't modify the Request we were given.
// This is required by the specification of http.RoundTripper.
req = cloneRequest(req)
>> req.Form.Set("foo", bar)
// Make the HTTP request.
return t.transport().RoundTrip(req)
}
// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
*r2 = *r
// deep copy of the Header
r2.Header = make(http.Header)
for k, s := range r.Header {
r2.Header[k] = s
}
return r2
}
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:
err := req.ParseForm()
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
Form
、PostForm
和ParseForm()
只在接收请求时使用。在发送请求时,传输层期望数据已经被正确编码。
你的想法是正确的,通过包装RoundTrip
来实现,但你必须自己处理编码后的数据。
if req.URL.RawQuery == "" {
req.URL.RawQuery = "foo=bar"
} else {
req.URL.RawQuery = req.URL.RawQuery + "&" + "foo=bar"
}
或者可以选择以下方式:
form, _ = url.ParseQuery(req.URL.RawQuery)
form.Add("boo", "far")
req.URL.RawQuery = form.Encode()
如果你想避免重复的键,也可以事先检查url.Values
。另外,检查Content-Type
头部是否为multipart/form-data
或application/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.
if req.URL.RawQuery == "" {
req.URL.RawQuery = "foo=bar"
} else {
req.URL.RawQuery = req.URL.RawQuery + "&" + "foo=bar"
}
or alternatively:
form, _ = url.ParseQuery(req.URL.RawQuery)
form.Add("boo", "far")
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论