http - redirección - uri de redireccionamiento autorizados
¿Cómo puedo hacer que el cliente HTTP Go NO siga los redireccionamientos automáticamente? (4)
Actualmente estoy escribiendo un software en Go que interactúa con una API REST. El punto final de la API REST que estoy tratando de consultar devuelve un redireccionamiento HTTP 302 junto con un encabezado de Ubicación HTTP, apuntando a un URI de recursos.
Intento usar mi script de Go para tomar el encabezado de Ubicación HTTP para su posterior procesamiento.
Esto es lo que estoy haciendo actualmente para lograr esta funcionalidad:
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()
}
Esto se siente como un truco para mí. Al anular la función CheckRedirect
, estoy esencialmente obligado a tratar los redireccionamientos HTTP como los errores (que no son).
He visto varios otros lugares que sugieren utilizar un transporte HTTP en lugar de un cliente HTTP, pero no estoy seguro de cómo hacerlo funcionar, ya que necesito el Cliente HTTP, ya que necesito utilizar HTTP Basic Auth para comunicarme con este RESTO. API.
¿Puede alguno de ustedes decirme una forma de realizar solicitudes HTTP con Autenticación básica, sin seguir los redireccionamientos, que no implique errores de arrojo y manejo de errores?
Gracias.
Es posible, pero la solución invierte un poco el problema. Aquí hay una muestra escrita como prueba de 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
}
Debería poder poner el código anterior en su propio paquete llamado "redirects" y ejecutarlo luego de obtener las dependencias requeridas usando
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
¡Espero que esto ayude!
Hay una solución mucho más simple en este momento:
client: &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
De esta forma, el paquete http
automáticamente sabe: "Ah, no debería seguir ninguna redirección", pero no arroja ningún error. Del comentario en el código fuente:
Como un caso especial, si CheckRedirect devuelve ErrUseLastResponse, la respuesta más reciente se devuelve con su cuerpo sin cerrar, junto con un error nulo.
Otra opción, usar el cliente por sí mismo, sin el 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
}
ACTUALIZACIÓN: esta solución es para ir <1.7
Para realizar una solicitud con autenticación básica que no sigue redirigir utilice la función RoundTrip que acepta * Request
Este código
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))
}
salidas
{
"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"
}
}