ASP.NET MVC TempData в файле cookie браузера - PullRequest
5 голосов
/ 28 сентября 2010

Я пытаюсь использовать собственного ITempDataProvider провайдера для хранения TempData в файле cookie браузера вместо состояния сеанса.Тем не менее, все работает нормально, за исключением того, что я не могу удалить cookie из потока ответа после прочтения.

Есть идеи?
Спасибо!

public class CookieTempDataProvider : ITempDataProvider
    {
        internal const string TempDataCookieKey = "__ControllerTempData";
        HttpContextBase _httpContext;

        public CookieTempDataProvider(HttpContextBase httpContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }
            _httpContext = httpContext;
        }

        public HttpContextBase HttpContext
        {
            get
            {
                return _httpContext;
            }
        }

        protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
        {
            HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
            if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
            {
                IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value);

                // Remove cookie                
                cookie.Expires = DateTime.MinValue;
                cookie.Value = string.Empty;
                _httpContext.Request.Cookies.Remove(TempDataCookieKey);

                if (_httpContext.Response != null && _httpContext.Response.Cookies != null)
                {
                    HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey];
                    if (responseCookie != null)
                    {
                        // Remove cookie
                        cookie.Expires = DateTime.MinValue;
                        cookie.Value = string.Empty;
                        _httpContext.Response.Cookies.Remove(TempDataCookieKey);

                    }
                }

                return deserializedTempData;
            }

            return new Dictionary<string, object>();
        }

        protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
        {

            string cookieValue = SerializeToBase64EncodedString(values);  
            var cookie = new HttpCookie(TempDataCookieKey);
            cookie.HttpOnly = true;
            cookie.Value = cookieValue;

            _httpContext.Response.Cookies.Add(cookie);
        }

        public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)
        {
            byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
            var memStream = new MemoryStream(bytes);
            var binFormatter = new BinaryFormatter();
            return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/;
        }

        public static string SerializeToBase64EncodedString(IDictionary<string, object> values)
        {
            MemoryStream memStream = new MemoryStream();
            memStream.Seek(0, SeekOrigin.Begin);
            var binFormatter = new BinaryFormatter();
            binFormatter.Serialize(memStream, values);
            memStream.Seek(0, SeekOrigin.Begin);
            byte[] bytes = memStream.ToArray();
            return Convert.ToBase64String(bytes);
        }

        IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext)
        {
            return LoadTempData(controllerContext);
        }

        void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
        {
            SaveTempData(controllerContext, values);
        }
    }

Ответы [ 3 ]

4 голосов
/ 05 мая 2013

Существует лучшее решение Брока Аллена для GitHub, которое использует шифрование, 2 формы сериализации и сжатие для защиты и оптимизации файлов cookie.

https://github.com/brockallen/CookieTempData

Вот ссылка на блог об этом:

http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/

У него также есть хороший метод, использующий IControllerFactory, чтобы гарантировать, что каждый контроллер снабжен экземпляром ITempDataProvider.

3 голосов
/ 15 марта 2011

Привет, у меня тоже была такая же проблема, и это была проблема с реализацией CookieTempDataProvider.

Итак, я немного изменил код, и теперь он работает отлично.

Когда он читает данные из файла cookie, он удаляет их как из запроса, так и из ответа. Но добавьте еще один файл cookie с пустым значением в функцию SaveData, которая вызывается после завершения обработки запроса.

Обращает на себя внимание: если вы хотите удалить куки, вам нужно установить значение тайм-аута и отправить его обратно клиенту, а затем браузер удалит его. Мы не можем сделать это иначе из кода, который cookie обрабатывает браузер

И я обнаружил, что установка срока действия DateTime.MinValue не истекает cookie в chrome (не знаю о других браузерах), поэтому я установил его на 2001-01-01:)

Вот рабочий код

public class CookieTempDataProvider : ITempDataProvider
{
    internal const string TempDataCookieKey = "__ControllerTempData";
    HttpContextBase _httpContext;

    public CookieTempDataProvider(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }
        _httpContext = httpContext;
    }

    public HttpContextBase HttpContext
    {
        get
        {
            return _httpContext;
        }
    }

    protected virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
        if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey)) //we need this because
        //Cookies[TempDataCookieKey] will create the cookie if it does not exist
        {
            HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];
            if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
            {
                IDictionary<string, object> deserializedTempData = DeserializeTempData(cookie.Value);

                // Remove cookie                
                cookie.Expires = new DateTime(2000, 1, 1);
                cookie.Value = string.Empty;
                _httpContext.Request.Cookies.Remove(TempDataCookieKey);

                if (_httpContext.Response != null && _httpContext.Response.Cookies != null)
                {
                    HttpCookie responseCookie = _httpContext.Response.Cookies[TempDataCookieKey];
                    if (responseCookie != null)
                    {
                        // Remove cookie
                        cookie.Expires = new DateTime(2000, 1, 1);
                        cookie.Value = string.Empty;
                        _httpContext.Response.Cookies.Remove(TempDataCookieKey);

                    }
                }

                return deserializedTempData;
            }
        }
        return new Dictionary<string, object>();
    }

    protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
        if (values != null && values.Count > 0)
        {
            //there are values to set, so add the cookie. But no need to expire it as we need the browser to send the 
            //cookie back with the next request
            string cookieValue = SerializeToBase64EncodedString(values);
            var cookie = new HttpCookie(TempDataCookieKey);
            cookie.HttpOnly = true;
            cookie.Value = cookieValue;

            _httpContext.Response.Cookies.Add(cookie);
        }
        else
        {
            //Still we need to add the cookie with the expiration set, to make the client browser remove the cookie from the request.
            //Otherwise the browser will continue to send the cookie with the response

            //Also we need to do this only if the requet had a tempdata cookie

            if (_httpContext.Request.Cookies.AllKeys.Contains(TempDataCookieKey))
            {
                {
                    HttpCookie cookie = _httpContext.Request.Cookies[TempDataCookieKey];

                    // Remove the request cookie                
                    cookie.Expires = new DateTime(2000, 1, 1);
                    cookie.Value = string.Empty;
                    _httpContext.Request.Cookies.Remove(TempDataCookieKey);

                    var rescookie = new HttpCookie(TempDataCookieKey);
                    rescookie.HttpOnly = true;
                    rescookie.Value = "";
                    rescookie.Expires = new DateTime(2000, 1, 1); //so that the browser will remove the cookie when it receives the request
                    _httpContext.Response.Cookies.Add(rescookie);
                }
            }
        }
    }

    public static IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)
    {
        byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
        var memStream = new MemoryStream(bytes);
        var binFormatter = new BinaryFormatter();
        return binFormatter.Deserialize(memStream, null) as IDictionary<string, object> /*TempDataDictionary : This returns NULL*/;
    }

    public static string SerializeToBase64EncodedString(IDictionary<string, object> values)
    {
        MemoryStream memStream = new MemoryStream();
        memStream.Seek(0, SeekOrigin.Begin);
        var binFormatter = new BinaryFormatter();
        binFormatter.Serialize(memStream, values);
        memStream.Seek(0, SeekOrigin.Begin);
        byte[] bytes = memStream.ToArray();
        return Convert.ToBase64String(bytes);
    }

    IDictionary<string, object> ITempDataProvider.LoadTempData(ControllerContext controllerContext)
    {
        return LoadTempData(controllerContext);
    }

    void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
        SaveTempData(controllerContext, values);
    }
}
2 голосов
/ 03 июня 2012

Вот пример рабочего решения без большого количества лишнего кода.Он использует Json.NET для сериализации, которая работает быстрее, чем BinaryFormatter + Base64Encoding, а также создает более короткую строку намного (= меньше издержек http).

public class CookieTempDataProvider : ITempDataProvider
{
    const string cookieKey = "temp";

    public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
        var cookie = controllerContext.HttpContext.Request.Cookies[cookieKey];   

        if (cookie != null) {
            return JsonConvert.DeserializeObject<IDictionary<string, object>>(cookie.Value);
        }

        return null;
    }

    // Method is called after action execution. The dictionary mirrors the contents of TempData.
    // If there are any values in the dictionary, save it in a cookie. If the dictionary is empty,
    // remove the cookie if it exists.
    public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
        var ctx = controllerContext.HttpContext;

        if (values.Count > 0) {
            var cookie = new HttpCookie(cookieKey)
            {
                HttpOnly = true,
                Value = JsonConvert.SerializeObject(values)
            };

            ctx.Response.Cookies.Add(cookie);
        } else if (ctx.Request.Cookies[cookieKey] != null) {

            // Expire cookie to remove it from browser.
            ctx.Response.Cookies[cookieKey].Expires = DateTime.Today.AddDays(-1);
        }
    }
}
...