Windows Phone 7 Mango сохраняет состояние CookieContainer

update1: После дополнительных исследований я не уверен, что это возможно, Я создал запись UserVoice при исправлении ошибки.

Я пытаюсь сохранить CookieContainer при выходе из приложения или когда происходит захоронение, но я столкнулся с некоторыми проблемами.

Я пытался сохранить CookieContainer в AppSettings, но при загрузке файлы cookie ушел.

Researching this internally, DataContractSerializer cannot serialize cookies.
This seems to be a behavior that Windows Phone inherited from Silverlight's DataContractSerializer.

После проведения дополнительных исследований выяснилось, что обходной путь заключается в том, чтобы вытащить печенье из контейнера и сохранить его другим способом. Это работало нормально, пока я не наткнулся на другую загвоздку. Я не могу получить файлы cookie с Uri .mydomain.com. Я полагаю, что это из-за этой ошибки. Я вижу файл cookie .mydomain.com в таблице доменов, но GetCookies не работает с этим конкретным файлом cookie.

Ошибка снова опубликована здесь.

Также существует проблема с получением файлов cookie из контейнера, когда домен начинается с .:

CookieContainer container = new CookieContainer();
container.Add(new Cookie("x", "1", "/", ".blah.com"));
CookieCollection cv = container.GetCookies(new Uri("http://blah.com"));
cv = container.GetCookies(new Uri("http://w.blah.com"));

Я нашел обходной путь для этого, используя отражение для итерации доменной таблицы и удаления '.' префикс.

private void BugFix_CookieDomain(CookieContainer cookieContainer)
{
    System.Type _ContainerType = typeof(CookieContainer);
    var = _ContainerType.InvokeMember("m_domainTable",
                               System.Reflection.BindingFlags.NonPublic |
                               System.Reflection.BindingFlags.GetField |
                               System.Reflection.BindingFlags.Instance,
                               null,
                               cookieContainer,
                               new object[] { });
    ArrayList keys = new ArrayList(table.Keys);
    foreach (string keyObj in keys)
    {
        string key = (keyObj as string);
        if (key[0] == '.')
        {
            string newKey = key.Remove(0, 1);
            table[newKey] = table[keyObj];
        }
    }
}

Только при вызове InvokeMember в SL генерируется исключение MethodAccessException. На самом деле это не решает мою проблему, поскольку одним из файлов cookie, которые мне нужно сохранить, является HttpOnly, что является одной из причин для CookieContainer.

Если сервер отправляет файлы cookie HTTPOnly, вы должны создать System.Net.CookieContainer для запроса на хранение файлов cookie, хотя вы не увидите файлы cookie, хранящиеся в контейнере, и не сможете получить к ним доступ.

Итак, есть идеи? Я пропустил что-то простое? Есть ли другой способ сохранить состояние CookieContainer или мне нужно сохранить информацию о пользователях, включая пароль, и повторно аутентифицировать их каждый раз при запуске приложения и при возвращении из захоронения?


person Derek Beattie    schedule 27.09.2011    source источник


Ответы (2)


Вы не можете получить доступ к закрытым членам за пределами вашей сборки в WP7, даже с Reflection. Это мера безопасности, гарантирующая, что вы не сможете вызывать внутренние системные API.

Похоже, вам может не повезти.

person Richard Szalay    schedule 27.09.2011
comment
Да, вы могли бы подумать, что это будет что-то просто сделать. - person Derek Beattie; 27.09.2011

Я написал CookieSerializer, специально предназначенный для решения этой проблемы. Сериализатор вставлен ниже. Рабочий проект и сценарий можно найти на сайте CodePlex.

public static class CookieSerializer
{
    /// <summary>
    /// Serializes the cookie collection to the stream.
    /// </summary>
    /// <param name="cookies">You can obtain the collection through your <see cref="CookieAwareWebClient">WebClient</see>'s <code>CookieContainer.GetCookies(Uri)</code>-method.</param>
    /// <param name="address">The <see cref="Uri">Uri</see> that produced the cookies</param>
    /// <param name="stream">The stream to which to serialize</param>
    public static void Serialize(CookieCollection cookies, Uri address, Stream stream)
    {
        using (var writer = new StreamWriter(stream))
        {
            for (var enumerator = cookies.GetEnumerator(); enumerator.MoveNext();)
            {
                var cookie = enumerator.Current as Cookie;
                if (cookie == null) continue;
                writer.WriteLine(address.AbsoluteUri);
                writer.WriteLine(cookie.Comment);
                writer.WriteLine(cookie.CommentUri == null ? null : cookie.CommentUri.AbsoluteUri);
                writer.WriteLine(cookie.Discard);
                writer.WriteLine(cookie.Domain);
                writer.WriteLine(cookie.Expired);
                writer.WriteLine(cookie.Expires);
                writer.WriteLine(cookie.HttpOnly);
                writer.WriteLine(cookie.Name);
                writer.WriteLine(cookie.Path);
                writer.WriteLine(cookie.Port);
                writer.WriteLine(cookie.Secure);
                writer.WriteLine(cookie.Value);
                writer.WriteLine(cookie.Version);
            }
        }
    }

    /// <summary>
    /// Deserializes <see cref="Cookie">Cookie</see>s from the <see cref="Stream">Stream</see>, 
    /// filling the <see cref="CookieContainer">CookieContainer</see>.
    /// </summary>
    /// <param name="stream">Stream to read</param>
    /// <param name="container">Container to fill</param>
    public static void Deserialize(Stream stream, CookieContainer container)
    {
        using (var reader = new StreamReader(stream))
        {
            while (!reader.EndOfStream)
            {
                var uri = Read(reader, absoluteUri => new Uri(absoluteUri, UriKind.Absolute));
                var cookie = new Cookie();
                cookie.Comment = Read(reader, comment => comment);
                cookie.CommentUri = Read(reader, absoluteUri => new Uri(absoluteUri, UriKind.Absolute));
                cookie.Discard = Read(reader, bool.Parse);
                cookie.Domain = Read(reader, domain => domain);
                cookie.Expired = Read(reader, bool.Parse);
                cookie.Expires = Read(reader, DateTime.Parse);
                cookie.HttpOnly = Read(reader, bool.Parse);
                cookie.Name = Read(reader, name => name);
                cookie.Path = Read(reader, path => path);
                cookie.Port = Read(reader, port => port);
                cookie.Secure = Read(reader, bool.Parse);
                cookie.Value = Read(reader, value => value);
                cookie.Version = Read(reader, int.Parse);
                container.Add(uri, cookie);
            }
        }
    }

    /// <summary>
    /// Reads a value (line) from the serialized file, translating the string value into a specific type
    /// </summary>
    /// <typeparam name="T">Target type</typeparam>
    /// <param name="reader">Input stream</param>
    /// <param name="translator">Translation function - translate the read value into 
    /// <typeparamref name="T"/> if the read value is not <code>null</code>.
    /// <remarks>If the target type is <see cref="Uri">Uri</see> , the value is considered <code>null</code> if it's an empty string.</remarks> </param>
    /// <param name="defaultValue">The default value to return if the read value is <code>null</code>.
    /// <remarks>The translation function will not be called for null values.</remarks></param>
    /// <returns></returns>
    private static T Read<T>(TextReader reader, Func<string, T> translator, T defaultValue = default(T))
    {
        var value = reader.ReadLine();
        if (value == null)
            return defaultValue;
        if (typeof(T) == typeof(Uri) && String.IsNullOrEmpty(value))
            return defaultValue;
        return translator(value);
    }
}
person Sami Lamti    schedule 09.12.2011
comment
Это работает с wp7? Я не думал, что wp7 поддерживает файлы cookie HTTPOnly. - person William Melani; 09.12.2011
comment
Файлы cookie HTTPOnly будут присутствовать, но они будут скрыты, даже если вы не сможете прочитать код. Что вы можете сделать: вы можете переназначать один и тот же CookieContainer снова и снова со всеми последующими запросами. - person Somnath; 24.01.2012