更改 *http.Client 的传输方式

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

Change *http.Client transport

问题

现状

我选择了一个边项目(构建第三方API的包装器),但卡住了。我正在使用sling来组合我的HTTP请求。

所以客户端的部分如下所示:

type Client struct {
    // 一些服务等...
    sling *sling.Sling <-- 这是用*http.Client初始化的
}

func NewClient(httpClient *http.Client) *Client {
    sling := sling.New().Client(httpClient).Base(BaseURL)
}

//....

我无法理解的事情

我遵循与go-githubgo-twitter相同的原则,即认证不应由我的库处理,而应由golang的oauth1/2包处理。

由于API提供应用程序级和用户级认证,并且某些工作流程需要初始的应用程序级认证,然后是用户级认证,我的问题是,是否有任何方法可以更改*http.Transport以便在客户端基础上更改身份验证标头。

到目前为止,我还没有找到方法来实现这一点。

英文:

The Status Quo

Having picked a side project (building a wrapper around a third party API), I'm stuck. I am using sling to compose my HTTP requests.

So parts of the Client are composed as follows:

type Client struct {
    // some services etc..
    sling *sling.Sling <-- this is initialized with *http.Client
}

func NewClient(httpClient *http.Client) *Client {
    sling := sling.New().Client(httpClient).Base(BaseURL)
}

//....

Things I can't wrap my head around

I am following the same principle as go-github and go-twitter that authentication should not be handled by my library, but rather by golangs oauth1/2 package.

As the the API provides application and user level authentication and some of the workflows require initial application level authentication and then user level authentication, my question is, if there is any way to change the *http.Transport in order to change the authentication header on a client basis.

So far, I haven't found a way to do so.

答案1

得分: 13

http.Client有一个Transport字段,你可以使用它来"在客户端基础上更改身份验证标头",如果这是你想要的。Transport字段的类型是http.RoundTripper,它是一个只有一个方法的接口,所以你只需要定义一个实现RoundTrip方法的传输。

type MyTransport struct {
    apiKey string
    // 保留对客户端原始传输的引用
    rt http.RoundTripper
}

func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    // 在这里设置你的身份验证标头
    r.Header.Set("Auth", t.apiKey)
    return t.rt.RoundTrip(r)
}

现在,你可以使用这个类型的实例来设置http.ClientTransport字段。

var client *http.Client = // 从某个地方获取客户端...
// 将传输设置为你的类型
client.Transport = &MyTransport{apiKey: "secret", rt: client.Transport}

根据你获取客户端的方式和位置,可能客户端的Transport字段尚未设置,所以在这种情况下,确保你的类型使用默认传输可能是一个好主意。

func (t *MyTransport) transport() http.RoundTripper {
    if t.rt != nil {
        return t.rt
    }
    return http.DefaultTransport
}

// 根据需要更新你的方法
func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    // 在这里设置你的身份验证标头
    r.Header.Set("Auth", t.apiKey)
    return t.transport().RoundTrip(r)
}

值得注意的是,Go文档建议不要在RoundTrip方法内修改*http.Request,所以你可以做的,以及你提供的go-github包正在做的,是创建一个请求的副本,在副本上设置身份验证标头,并将其传递给底层的Transport。参见这里:https://github.com/google/go-github/blob/d23570d44313ca73dbcaadec71fc43eca4d29f8b/github/github.go#L841-L875

英文:

The http.Client has a Transport field that you could use to "change the authentication header on a client basis" if that's what you want. The Transport field has type http.RoundTripper which is a one method interface, so all you need to do is to define your transport with an implementation of the RoundTrip method.

type MyTransport struct {
    apiKey string
    // keep a reference to the client's original transport
    rt http.RoundTripper
}

func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    // set your auth headers here
    r.Header.Set("Auth", t.apiKey)
    return t.rt.RoundTrip(r)
}

Now you can use an instance of this type to set the Transport field on an http.Client.

var client *http.Client = // get client from somewhere...
// set the transport to your type
client.Transport = &MyTransport{apiKey: "secret", tr: client.Transport}

Depending on how and where from you got the client, it's possible that its Transport field is not yet set, so it might be a good idea to ensure that your type uses the default transport in such a case.

func (t *MyTransport) transport() http.RoundTripper {
	if t.rt != nil {
		return t.rt
	}
	return http.DefaultTransport
}

// update your method accordingly
func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    // set your auth headers here
    r.Header.Set("Auth", t.apiKey)
    return t.transport().RoundTrip(r)
}

It might be worth noting that the Go documentation recommends not to modify the *http.Request inside the RoundTrip method, so what you can do, and what the go-github package you linked to is doing, is to create a copy of the request, set the auth headers on it, and pass that to the underlying Transport. See here: https://github.com/google/go-github/blob/d23570d44313ca73dbcaadec71fc43eca4d29f8b/github/github.go#L841-L875

huangapple
  • 本文由 发表于 2017年4月17日 15:48:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/43447405.html
匿名

发表评论

匿名网友

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

确定