Подтвердить что ты не робот

Как я могу сделать HTTP-клиент Go НЕ ПЕРЕДАЮТСЯ автоматически?

В настоящее время я пишу какое-то программное обеспечение в Go, которое взаимодействует с REST API. Конечная точка REST API, с которой я пытаюсь выполнить запрос, возвращает перенаправление HTTP 302 вместе с заголовком HTTP-местоположения, указывая на URI ресурса.

Я пытаюсь использовать мой Go script, чтобы захватить заголовок HTTP-адреса для последующей обработки.

Вот что я сейчас делаю для достижения этой функциональности в настоящее время:

package main

import (
        "errors"
        "fmt"
        "io/ioutil"
        "net/http"
)

var BASE_URL = "https://api.stormpath.com/v1"
var STORMPATH_API_KEY_ID = "xxx"
var STORMPATH_API_KEY_SECRET = "xxx"

func noRedirect(req *http.Request, via []*http.Request) error {
        return errors.New("Don't redirect!")
}

func main() {

        client := &http.Client{
            CheckRedirect: noRedirect
        }
        req, err := http.NewRequest("GET", BASE_URL+"/tenants/current", nil)
        req.SetBasicAuth(STORMPATH_API_KEY_ID, STORMPATH_API_KEY_SECRET)

        resp, err := client.Do(req)

        // If we get here, it means one of two things: either this http request
        // actually failed, or we got an http redirect response, and should process it.
        if err != nil {
            if resp.StatusCode == 302 {
                fmt.Println("got redirect")
            } else {
                panic("HTTP request failed.")
            }
        }
        defer resp.Body.Close()

}

Это кажется для меня чем-то вроде взлома. Переопределяя функцию http.Client CheckRedirect, я по существу вынужден рассматривать HTTP-перенаправления как ошибки (которых они не являются).

Я видел несколько других мест, предлагающих использовать HTTP-транспорт вместо HTTP-клиента, но я не уверен, как это сделать, поскольку мне нужен HTTP-клиент, поскольку мне нужно использовать HTTP Basic Auth для связи с этим API REST.

Может ли кто-нибудь из вас рассказать мне, как сделать HTTP-запросы с базовой аутентификацией - в то время как не следовать переадресации - это не связано с ошибками с ошибками и обработкой ошибок?

Спасибо.

4b9b3361

Ответ 1

Там гораздо более простое решение:

client: &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    },
}

Таким образом, пакет http автоматически распознает: "Ах, я не должен следовать никаким перенаправлениям", но не вызывает никаких ошибок. Из комментария в исходном коде:

В качестве специального случая, если CheckRedirect возвращает ErrUseLastResponse, то последний ответ возвращается с его телом незамкнутый, вместе с ошибкой nil.

Ответ 2

Другой вариант, используя клиент, без RoundTrip:

// create a custom error to know if a redirect happened
var RedirectAttemptedError = errors.New("redirect")

client := &http.Client{}
// return the error, so client won't attempt redirects
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
        return RedirectAttemptedError
}
// Work with the client...
resp, err := client.Head(urlToAccess)

// test if we got the custom error
if urlError, ok := err.(*url.Error); ok && urlError.Err == RedirectAttemptedError{
        err = nil   
}

UPDATE: это решение для go < 1.7

Ответ 3

Возможно, но решение мало меняет проблему. Здесь образец, записанный как тест golang.

package redirects

import (
    "github.com/codegangsta/martini-contrib/auth"
    "github.com/go-martini/martini"
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestBasicAuthRedirect(t *testing.T) {
    // Start a test server
    server := setupBasicAuthServer()
    defer server.Close()

    // Set up the HTTP request
    req, err := http.NewRequest("GET", server.URL+"/redirect", nil)
    req.SetBasicAuth("username", "password")
    if err != nil {
        t.Fatal(err)
    }

    transport := http.Transport{}
    resp, err := transport.RoundTrip(req)
    if err != nil {
        t.Fatal(err)
    }
    // Check if you received the status codes you expect. There may
    // status codes other than 200 which are acceptable.
    if resp.StatusCode != 200 && resp.StatusCode != 302 {
        t.Fatal("Failed with status", resp.Status)
    }

    t.Log(resp.Header.Get("Location"))
}


// Create an HTTP server that protects a URL using Basic Auth
func setupBasicAuthServer() *httptest.Server {
    m := martini.Classic()
    m.Use(auth.Basic("username", "password"))
    m.Get("/ping", func() string { return "pong" })
    m.Get("/redirect", func(w http.ResponseWriter, r *http.Request) {
        http.Redirect(w, r, "/ping", 302)
    })
    server := httptest.NewServer(m)
    return server
}

Вы должны уметь включить в него собственный пакет под названием "redirects" и запустить его после получения необходимых зависимостей с помощью

mkdir redirects
cd redirects
# Add the above code to a file with an _test.go suffix
go get github.com/codegangsta/martini-contrib/auth
go get github.com/go-martini/martini
go test -v

Надеюсь, это поможет!

Ответ 4

Чтобы сделать запрос с Basic Auth, который не следует за перенастройкой, используйте RoundTrip функцию, которая принимает * Request

Этот код

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

func main() {
    var DefaultTransport http.RoundTripper = &http.Transport{}

    req, _ := http.NewRequest("GET", "http://httpbin.org/headers", nil)
    req.SetBasicAuth("user", "password")

    resp, _ := DefaultTransport.RoundTrip(req)
    defer resp.Body.Close()
    contents, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("%s", err)
        os.Exit(1)
    }
    fmt.Printf("%s\n", string(contents))
}

выходы

{
  "headers": {
    "Accept-Encoding": "gzip", 
    "Authorization": "Basic dXNlcjpwYXNzd29yZA==", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "Go 1.1 package http", 
    "X-Request-Id": "45b512f1-22e9-4e49-8acb-2f017e0a4e35"
  }
}