HttpWebResponse.Cookies пуст, несмотря на Set-Cookie Header (без перенаправления)
Я изо всех сил пытаюсь понять, что здесь не так. Я отправляю регистрационную информацию, я вижу Set-Cookie в заголовке с правильным значением, но коллекция Cookies не заполняется.
Это HTTPS, авто-перенаправления входа, но я отключил его с помощью AllowAutoRedirect = false, чтобы попытаться устранить эту проблему.
На этом снимке экрана вы можете легко увидеть отладочную информацию и установить cookie. Я устанавливаю свой httpWebRequest.Cookie в новый CookieCollection.
![Right click and select view image to see full-size.]()
HttpWebRequest httpRequest;
CookieContainer reqCookies = new CookieContainer();
string url = "https://example.com";
string[] email = user.Split('@');
email[0] = System.Web.HttpUtility.UrlEncode(email[0]);
user = email[0] + "@" + email[1];
pass = System.Web.HttpUtility.UrlEncode(pass);
string postData = "email=" + user + "&password=" + pass;
byte[] byteData = Encoding.UTF8.GetBytes(postData);
httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.Method = "POST";
httpRequest.Referer = url;
httpRequest.CookieContainer = reqCookies;
httpRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1003.1 Safari/535.19";
httpRequest.Accept = "text/html, application/xhtml+xml, */*";
httpRequest.ContentType = "application/x-www-form-urlencoded";
httpRequest.ContentLength = byteData.Length;
using (Stream postStream = httpRequest.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
postStream.Close();
}
httpRequest.AllowAutoRedirect = false;
HttpWebResponse b = (HttpWebResponse)httpRequest.GetResponse();
Пробовал тот же самый код, связанный с http://www.yahoo.com, и файлы cookie помещаются в мою коллекцию... Argh...
Вот значение заголовка Set-Cookie:
s = 541E2101-B768-45C8-B814-34A00525E50F; Домен = example.com; Path =/; Version = 1
Ответы
Ответ 1
ОБНОВЛЕНИЕ пять лет спустя кто-то на самом деле упомянул правильный способ сделать это: правильно настроить CookieContainer и позволить ему обрабатывать все. Пожалуйста, обратитесь к решению Сэма ниже.
Я также обнаружил эту проблему при чтении Cookies в С#, которые были созданы приложением С# ASP.NET...;)
Не уверен, имеет ли это отношение к этому, но я обнаружил, что два Cookies, которые установлены в моем случае, записаны в одном заголовке Set-Cookie с полезной нагрузкой cookie, разделенной запятыми. Поэтому я адаптировал решение AppDeveloper для решения этой проблемы с несколькими файлами cookie, а также для исправления названия/значения, о котором я упоминал в комментариях.
private static void fixCookies(HttpWebRequest request, HttpWebResponse response)
{
for (int i = 0; i < response.Headers.Count; i++)
{
string name = response.Headers.GetKey(i);
if (name != "Set-Cookie")
continue;
string value = response.Headers.Get(i);
foreach (var singleCookie in value.Split(','))
{
Match match = Regex.Match(singleCookie, "(.+?)=(.+?);");
if (match.Captures.Count == 0)
continue;
response.Cookies.Add(
new Cookie(
match.Groups[1].ToString(),
match.Groups[2].ToString(),
"/",
request.Host.Split(':')[0]));
}
}
}
Ответ 2
Кажется, что заголовок Set-Cookie
отправленный веб-сайтом, искажен (не в типичном формате, который должен был быть).
В таком случае вам необходимо вручную CookieContainer
файл cookie, а это нужно для CookieContainer
.
for (int i = 0; i < b.Headers.Count; i++)
{
string name = b.Headers.GetKey(i);
string value = b.Headers.Get(i);
if (name == "Set-Cookie")
{
Match match = Regex.Match(value, "(.+?)=(.+?);");
if (match.Captures.Count > 0)
{
reqCookies.Add(new Cookie(match.Groups[1].Value, match.Groups[2].Value, "/", "example.com"));
}
}
}
Ответ 3
Используйте CookieContainer
как в этом ответе. То, что сработало для этого регулярного выражения, для меня было запятой в expires=Tue,...
Ответ 4
Правильный способ сделать это - установить член CookieContainer
до получения ответа:
var request = (HttpWebRequest)HttpWebRequest.Create(..);
request.CookieContainer = new CookieContainer();
var response = request.GetResponse();
// ..response.Cookies will now contain the cookies sent back by the server.
Вам не нужно вручную разбирать SetCookie
.
Ответ 5
Глядя на другие ответы, я улучшил неправильную обработку файлов cookie. В отличие от этих ответов, он автоматически обрабатывает все свойства cookie (например, истек, защищен и т.д.) И работает со всеми наборами файлов cookie (даже если имеется более 1 неправильного файла cookie).
Он реализован как метод расширения и может использоваться следующим образом:
//...
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
request.FixCookies(response);
//...
FixCookies()
расширения FixCookies()
:
using System;
using System.Collections.Generic;
using System.Net;
namespace AG.WebHelpers
{
static public class ExtensionMethods
{
static public void FixCookies(this HttpWebRequest request, HttpWebResponse response)
{
for (int i = 0; i < response.Headers.Count; i++)
{
string name = response.Headers.GetKey(i);
if (name != "Set-Cookie")
continue;
string value = response.Headers.Get(i);
var cookieCollection = ParseCookieString(value, () => request.Host.Split(':')[0]);
response.Cookies.Add(cookieCollection);
}
}
static private CookieCollection ParseCookieString(string cookieString, Func<string> getCookieDomainIfItIsMissingInCookie)
{
bool secure = false;
bool httpOnly = false;
string domainFromCookie = null;
string path = null;
string expiresString = null;
Dictionary<string, string> cookiesValues = new Dictionary<string, string>();
var cookieValuePairsStrings = cookieString.Split(';');
foreach(string cookieValuePairString in cookieValuePairsStrings)
{
var pairArr = cookieValuePairString.Split('=');
int pairArrLength = pairArr.Length;
for (int i = 0; i < pairArrLength; i++)
{
pairArr[i] = pairArr[i].Trim();
}
string propertyName = pairArr[0];
if (pairArrLength == 1)
{
if (propertyName.Equals("httponly", StringComparison.OrdinalIgnoreCase))
httpOnly = true;
else if (propertyName.Equals("secure", StringComparison.OrdinalIgnoreCase))
secure = true;
else
throw new InvalidOperationException(string.Format("Unknown cookie property \"{0}\". All cookie is \"{1}\"", propertyName, cookieString));
continue;
}
string propertyValue = pairArr[1];
if (propertyName.Equals("expires", StringComparison.OrdinalIgnoreCase))
expiresString = propertyValue;
else if (propertyName.Equals("domain", StringComparison.OrdinalIgnoreCase))
domainFromCookie = propertyValue;
else if (propertyName.Equals("path", StringComparison.OrdinalIgnoreCase))
path = propertyValue;
else
cookiesValues.Add(propertyName, propertyValue);
}
DateTime expiresDateTime;
if (expiresString != null)
{
expiresDateTime = DateTime.Parse(expiresString);
}
else
{
expiresDateTime = DateTime.MinValue;
}
if (string.IsNullOrEmpty(domainFromCookie))
{
domainFromCookie = getCookieDomainIfItIsMissingInCookie();
}
CookieCollection cookieCollection = new CookieCollection();
foreach (var pair in cookiesValues)
{
Cookie cookie = new Cookie(pair.Key, pair.Value, path, domainFromCookie);
cookie.Secure = secure;
cookie.HttpOnly = httpOnly;
cookie.Expires = expiresDateTime;
cookieCollection.Add(cookie);
}
return cookieCollection;
}
}
}
Ответ 6
Это может быть немного поздно, но вы можете использовать функцию SetCookies
var cHeader = responce.Headers.Get("Set-Cookie");
var cookie = new CookieContainer();
cookie.SetCookies(new Uri("[...]"), cHeader);
Ответ 7
Я знаю, что этот вопрос старый, но я натолкнулся на какой-то код, который правильно разбирает заголовок "Set-Cookie". Он обрабатывает файлы cookie, разделенные запятыми, и извлекает имя, истечение, путь, значение и домен каждого файла cookie.
Этот код работает лучше, чем собственный синтаксический анализатор файлов cookie Microsoft, и это действительно то, что должен делать официальный парсер cookie. Я не знаю, почему Microsoft еще не исправила это, так как это очень распространенная проблема.
Вот оригинальный код: http://snipplr.com/view/4427/
Я размещаю его здесь, если в какой-то момент ссылка идет вниз:
public static CookieCollection GetAllCookiesFromHeader(string strHeader, string strHost)
{
ArrayList al = new ArrayList();
CookieCollection cc = new CookieCollection();
if (strHeader != string.Empty)
{
al = ConvertCookieHeaderToArrayList(strHeader);
cc = ConvertCookieArraysToCookieCollection(al, strHost);
}
return cc;
}
private static ArrayList ConvertCookieHeaderToArrayList(string strCookHeader)
{
strCookHeader = strCookHeader.Replace("\r", "");
strCookHeader = strCookHeader.Replace("\n", "");
string[] strCookTemp = strCookHeader.Split(',');
ArrayList al = new ArrayList();
int i = 0;
int n = strCookTemp.Length;
while (i < n)
{
if (strCookTemp[i].IndexOf("expires=", StringComparison.OrdinalIgnoreCase) > 0)
{
al.Add(strCookTemp[i] + "," + strCookTemp[i + 1]);
i = i + 1;
}
else
{
al.Add(strCookTemp[i]);
}
i = i + 1;
}
return al;
}
private static CookieCollection ConvertCookieArraysToCookieCollection(ArrayList al, string strHost)
{
CookieCollection cc = new CookieCollection();
int alcount = al.Count;
string strEachCook;
string[] strEachCookParts;
for (int i = 0; i < alcount; i++)
{
strEachCook = al[i].ToString();
strEachCookParts = strEachCook.Split(';');
int intEachCookPartsCount = strEachCookParts.Length;
string strCNameAndCValue = string.Empty;
string strPNameAndPValue = string.Empty;
string strDNameAndDValue = string.Empty;
string[] NameValuePairTemp;
Cookie cookTemp = new Cookie();
for (int j = 0; j < intEachCookPartsCount; j++)
{
if (j == 0)
{
strCNameAndCValue = strEachCookParts[j];
if (strCNameAndCValue != string.Empty)
{
int firstEqual = strCNameAndCValue.IndexOf("=");
string firstName = strCNameAndCValue.Substring(0, firstEqual);
string allValue = strCNameAndCValue.Substring(firstEqual + 1, strCNameAndCValue.Length - (firstEqual + 1));
cookTemp.Name = firstName;
cookTemp.Value = allValue;
}
continue;
}
if (strEachCookParts[j].IndexOf("path", StringComparison.OrdinalIgnoreCase) >= 0)
{
strPNameAndPValue = strEachCookParts[j];
if (strPNameAndPValue != string.Empty)
{
NameValuePairTemp = strPNameAndPValue.Split('=');
if (NameValuePairTemp[1] != string.Empty)
{
cookTemp.Path = NameValuePairTemp[1];
}
else
{
cookTemp.Path = "/";
}
}
continue;
}
if (strEachCookParts[j].IndexOf("domain", StringComparison.OrdinalIgnoreCase) >= 0)
{
strPNameAndPValue = strEachCookParts[j];
if (strPNameAndPValue != string.Empty)
{
NameValuePairTemp = strPNameAndPValue.Split('=');
if (NameValuePairTemp[1] != string.Empty)
{
cookTemp.Domain = NameValuePairTemp[1];
}
else
{
cookTemp.Domain = strHost;
}
}
continue;
}
}
if (cookTemp.Path == string.Empty)
{
cookTemp.Path = "/";
}
if (cookTemp.Domain == string.Empty)
{
cookTemp.Domain = strHost;
}
cc.Add(cookTemp);
}
return cc;
}