HttpWebRequest y los problemas con las redirecciones y las cookies

¿Quién dijo que programar en .NET era coser y cantar? Hay muchas librerías que nos facilitan la vida pero a veces no funcionan del todo bien haciendo cosas raras o simplemente se les olvido incluir el control de ciertas situaciones.

Programando Squipy me he topado con un gran problema y es que cuando llego a una web que me hace una redirección (por ejemplo un 302) para que el HttpWebRequest la siga y obtenga luego la página a donde se redirige, he de poner AllowAutoRedirect = true

Pero si configuramos AllowAutoRedirect = true entonces no podremos capturar las cookies y, por tanto, no entrar a ciertas webs en las que necesitamos validación.

Si ponemos AllowAutoRedirect = false podremos capturar la cookie sin problemas usando GetResponseHeaderSet-Cookie«) pero no se hará la redirección y no llegaremos a la página a la que se redirige.

Así que la solución final pasa por poner la redirección a false, capturar la cookie con Set-Cookie y luego comprobar el StatusCode para ver si la página devuelve una redirección, en cuyo caso, incluiremos nuestra cookie con Headers.AddCookie«, nuestraCookie) y capturaremos la página a donde nos redirige con GetResponseHeaderLocation«). Tras esto crearemos manualmente otro HttpWebRequest para llamar a la nueva página.

Os dejo un código de ejemplo:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlDestino);
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

while (response.StatusCode == HttpStatusCode.Found)  {

String nuevaUrl = «http://» + response.ResponseUri.Host + response.GetResponseHeader(«Location«);
request = (HttpWebRequest) WebRequest.Create(nuevaUrl);
request.AllowAutoRedirect = false;
response = (HttpWebResponse) request.GetResponse();

}

A falta de comprobar con Set-Cookie si tenemos alguna cookie y guardarla en un contenedor de Cookies para luego usarla en el nuevo envío.

Feliz Año nuevo a todos!

5 comentarios

  1. Muy bueno el articulo, pero ando con un problema que no puedo solucionar.

    Estoy usando un CookieContainer para almacenar cookies porque no necesito modificarlas, solo loguearme en un sitio y mantenerme logueado peor no me esta funcionando bien. Después de hacer el POST al sitio me responde correctamente las cabeceras Set-Cookie con los datos de la cuenta.

    el problema viene cuando me redirecciona y quiero guardar las cookies que me dio antes en el nuevo request. Espiando las cabeceras HTTP con Fiddler veo que a paesar de que yo las guardo no se envían en las siguientes peticiones.

    Esta es la linea que pongo dentro del bucle, antes del response, para mantener las cookies de una request a otra.

    aRequest.CookieContainer = myCookieContainer;

    ¿Esta bien hecho?

  2. Lo que tienes que hacer es poner request.AllowAutoRedirect = false; para que no te redireccione. Luego capturas la web … algo asi:

    try
    {
    Stream objStream = req.GetResponse().GetResponseStream();
    StreamReader objReader = new StreamReader(objStream);

    String sLine = «»;
    String cad = «»;

    while (sLine != null)
    {
    sLine = objReader.ReadLine();

    if (sLine != null)
    cad += sLine + Environment.NewLine;
    }

    return cad;
    }
    catch
    {
    return null;
    }

    Y en cad tendrás el contenido del html con una redirección 301. Coges la url y hacer a mano la redirección con otro GET

  3. Perdona, que estoy releyendo lo que has puesto y no te contesté bien 🙂

    lo del cookiecontanier sería así:

    lo defines global:
    private CookieContainer ContenedorCookies = new CookieContainer();

    cuando capturas la cookie:
    ContenedorCookies.Add(webResponse.Cookies);

    y para usarlo:
    webRequest.CookieContainer = ContenedorCookies;

  4. Sigo sin lograrlo es asi como debo usarlo que decis del CookieContainer? Lo unico que me falla (creo) es reenviar la cookie en la redirección, que supuestamente lo estoy haciendo pero no funciona.

    static public void login(string username, string password)
    {
    HttpWebRequest aRequest;
    HttpWebResponse aResponse;
    CookieContainer myCookieContainer = new CookieContainer();

    String URI = «http://www.dynamicavatar.com/log.php»;
    String paramsPost = «username=» + username + «&password=» + password + «&login=Login»;

    // Hacemos el POST que despues vamos a enviar al servidor web
    aRequest = (HttpWebRequest) WebRequest.Create(URI);
    aRequest.Method = «POST»;
    aRequest.ContentType = «application/x-www-form-urlencoded»;
    aRequest.ContentLength = paramsPost.Length;
    aRequest.CookieContainer = myCookieContainer;
    aRequest.AllowAutoRedirect = false;
    aRequest.ServicePoint.Expect100Continue = false;

    //Realizamos el Post
    Stream stream = aRequest.GetRequestStream();
    stream.Write(Encoding.ASCII.GetBytes(paramsPost), 0, paramsPost.Length);
    stream.Flush();
    stream.Close();

    // Obtenesmos la respuesta del servidor
    aResponse = (HttpWebResponse) aRequest.GetResponse();
    Console.WriteLine(«httpResponse.StatusCode: {0}», aResponse.StatusCode); // [BORRAR] ESTA LINEA ES DE DEBUG

    // Si el server web nos redirige con un 302 lo seguimos con el bucle
    while (aResponse.StatusCode == HttpStatusCode.Found)
    {
    String nuevaURI = «http://» + aResponse.ResponseUri.Host + «/» + aResponse.GetResponseHeader(«Location»);
    aRequest = (HttpWebRequest) WebRequest.Create(nuevaURI);

    myCookieContainer.Add(aResponse.Cookies);
    aRequest.CookieContainer = myCookieContainer;

    aRequest.AllowAutoRedirect = false;
    aRequest.ServicePoint.Expect100Continue = false;

    aResponse = (HttpWebResponse) aRequest.GetResponse();
    Console.WriteLine(«httpResponse.StatusCode: {0}», aResponse.StatusCode); // [BORRAR] ESTA LINEA ES DE DEBUG
    }

    // Hacemos un GET al home para ver si estamos logueados
    aRequest = (HttpWebRequest) WebRequest.Create(«http://www.dynamicavatar.com/»);
    aRequest.Method = «GET»;
    aRequest.CookieContainer = myCookieContainer;

    aResponse = (HttpWebResponse) aRequest.GetResponse();

    // Ahora leemos el envio.
    StreamReader sr = new StreamReader(aResponse.GetResponseStream(), Encoding.ASCII);

    // Convertimos lo que obtenemos en String
    string s = sr.ReadToEnd();
    sr.Close();

    // Imprimimos la cadena
    Console.WriteLine(s);
    }

    PD: La pagina con la que estoy probando es de un amigo, no hacen spam, aviso por si alguien se suscribe.

  5. Está mal. Coges la cookie después de coger la web, es decir, en la segunda llamada y debes cogerla tras el POST y usarla en el GET … no se si me explico …

    myCookieContainer.Add(aResponse.Cookies);
    aRequest.CookieContainer = myCookieContainer;

Deja un comentario