Windows Phone 7 Mango, сохраняющий состояние CookieContainer - PullRequest
4 голосов
/ 27 сентября 2011

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

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

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

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 ]

2 голосов
/ 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);
    }
}
0 голосов
/ 27 сентября 2011

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

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

...