Golang擷取有重新導向資訊的response的location

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

在用golang做類比登入某個網站的功能時發現了一個問題:如何擷取該網站帶有重新導向資訊的Response?或者說我只需要
這個Response中的location資訊即可,但是發現對於go的標準庫net/http來說如果不hack這個庫是沒辦法直接擷取帶有重
定向資訊的Response,而且目前大多第三方的http庫也是基於標準庫net/http的,貌似就要改net/http庫或庫本身就提供了
對於重新導向的設定?

那麼我們就得先查看一下net/http庫的源碼了,通常我們都會用http.Client的Do方法發起一個請求,首先看一下它的源碼:

// Do sends an HTTP request and returns an HTTP response, following// policy (e.g. redirects, cookies, auth) as configured on the client.//// An error is returned if caused by client policy (such as// CheckRedirect), or if there was an HTTP protocol error.// A non-2xx response doesn't cause an error.//// When err is nil, resp always contains a non-nil resp.Body.//// Callers should close resp.Body when done reading from it. If// resp.Body is not closed, the Client's underlying RoundTripper// (typically Transport) may not be able to re-use a persistent TCP// connection to the server for a subsequent "keep-alive" request.//// The request Body, if non-nil, will be closed by the underlying// Transport, even on errors.//// Generally Get, Post, or PostForm will be used instead of Do.func (c *Client) Do(req *Request) (resp *Response, err error) {    if req.Method == "GET" || req.Method == "HEAD" {        return c.doFollowingRedirects(req, shouldRedirectGet)    }    if req.Method == "POST" || req.Method == "PUT" {        return c.doFollowingRedirects(req, shouldRedirectPost)    }    return c.send(req)}

由源碼的上方的注釋可發現Do方法是不會返回有重新導向資訊的Response的,只會返回已經重新導向跳轉成功後的Response,而且會根據是否CheckRedirect返回error云云
那麼假設我們要發起的是一個Get請求,所以進入c.doFollowingRedirects()方法中去看看

func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bool) (resp *Response, err error) {    var base *url.URL    redirectChecker := c.CheckRedirect    if redirectChecker == nil {        redirectChecker = defaultCheckRedirect    }    var via []*Request     if ireq.URL == nil {        ireq.closeBody()        return nil, errors.New("http: nil Request.URL")    }     var reqmu sync.Mutex // guards req    req := ireq     var timer *time.Timer    if c.Timeout > 0 {        type canceler interface {            CancelRequest(*Request)        }        tr, ok := c.transport().(canceler)        if !ok {            return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())        }        timer = time.AfterFunc(c.Timeout, func() {            reqmu.Lock()            defer reqmu.Unlock()            tr.CancelRequest(req)        })    }     urlStr := "" // next relative or absolute URL to fetch (after first request)    redirectFailed := false    for redirect := 0; ; redirect++ {        if redirect != 0 {            nreq := new(Request)            nreq.Method = ireq.Method            if ireq.Method == "POST" || ireq.Method == "PUT" {                nreq.Method = "GET"            }            nreq.Header = make(Header)            nreq.URL, err = base.Parse(urlStr)            if err != nil {                break            }            if len(via) > 0 {                // Add the Referer header.                lastReq := via[len(via)-1]                if ref := refererForURL(lastReq.URL, nreq.URL); ref != "" {                    nreq.Header.Set("Referer", ref)                }                 err = redirectChecker(nreq, via)                if err != nil {                    redirectFailed = true                    break                }            }            reqmu.Lock()            req = nreq            reqmu.Unlock()        }         urlStr = req.URL.String()        if resp, err = c.send(req); err != nil {            break        }          if shouldRedirect(resp.StatusCode) {             // Read the body if small so underlying TCP connection will be re-used.            // No need to check for errors: if it fails, Transport won't reuse it anyway.            const maxBodySlurpSize = 2 << 10            if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {                io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)            }            resp.Body.Close()            if urlStr = resp.Header.Get("Location"); urlStr == "" {                err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))                break            }             base = req.URL            via = append(via, req)            continue        }        if timer != nil {            resp.Body = &cancelTimerBody{timer, resp.Body}        }        return resp, nil    }     method := ireq.Method    urlErr := &url .Error{        Op:  method[0:1] + strings.ToLower(method[1:]),        URL: urlStr,        Err: err,    }     if redirectFailed {        // Special case for Go 1 compatibility: return both the response        // and an error if the CheckRedirect function failed.        // See http://golang.org/issue/3795        return resp, urlErr    }     if resp != nil {        resp.Body.Close()    }    return nil, urlErr}

在上述代碼中發現了有檢查是否進行重新導向的代碼:

redirectChecker := c.CheckRedirect    if redirectChecker == nil {        redirectChecker = defaultCheckRedirect    }

redirectChecker是一個這樣的函數:func(req Request, via []Request) error
發現了可在client中設定自己的redirectChecker,就是只要實現了func(req Request, via []Request) error即可,其功能是由源碼的
defaultCheckRedirect可知是控制重新導向跳轉的次數而已。

func defaultCheckRedirect(req *Request, via []*Request) error {    if len(via) >= 10 {        return errors.New("stopped after 10 redirects")    }    return nil}

defaultCheckRedirect中規定重新導向跳轉不能超過10次,否則返回error,那麼如果我們要禁止跳轉重新導向的話自己實現一個
CheckRedirect,把其中的10改成0即可。但是即使這樣設定後也是不能返回有重新導向資訊的Response,而是返回一個跳轉停
止的error,不過如果你需要的只是帶有重新導向資訊Response中的location資訊的話只需要從返回的error中提取即可。http庫
client中的doFollowingRedirects中傳入了一個shouldRedirect函數,這個函數正是根據各種http協議的代碼返回是否進行跳
轉的資訊,所以如果不改動這部分代碼沒法直接擷取有重新導向資訊的Response,這樣看來http庫的設定不夠靈活,得自己改或
另外實現了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.