Windows Phone 7 Mango, сохраняющий состояние CookieContainer

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

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

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

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

После дальнейших исследований мне показалось, что нужно было взять печенье из контейнера и сохранить его другим способом. Это работало нормально, пока я не наткнулся на другую загадку. Я не могу получить GetCookies с 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 или мне нужно сохранить информацию о пользователях, включая пароль, и повторно аутентифицировать их при каждом запуске приложения и при возврате из захоронения?

2 ответа

Решение

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

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

Я написал 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);
    }
}
Другие вопросы по тегам