ASP.NET MVC TempData in browser cookie

后端 未结 3 694
醉梦人生
醉梦人生 2021-01-13 08:41

I am trying to use a custom ITempDataProvider provider to store TempData in a browser\'s cookie instead of session state. However, everythi

3条回答
  •  天命终不由人
    2021-01-13 09:06

    Hi I too had the same issue and it was an issue with the implementation of CookieTempDataProvider.

    So I modified the code a bit and now it works perfectly.

    When it reads the data from the cookie, it removes it from both the request and response. But add another cookie with an empty value in the SaveData function which is called when the request processing is completed.

    Points to note : If you want to remove a cookie, you have to set the timeout value and send it back to the client and then the browser will remove it. We cannot do it otherwise from the code a the cookie is handled by the browser

    And I found out that setting the expiration to DateTime.MinValue does not expire the cookie in chrome (don't know about the other browsers) so I set it to 2001-01-01 :)

    Here is the working code

    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 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 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();
        }
    
        protected virtual void SaveTempData(ControllerContext controllerContext, IDictionary 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 DeserializeTempData(string base64EncodedSerializedTempData)
        {
            byte[] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
            var memStream = new MemoryStream(bytes);
            var binFormatter = new BinaryFormatter();
            return binFormatter.Deserialize(memStream, null) as IDictionary /*TempDataDictionary : This returns NULL*/;
        }
    
        public static string SerializeToBase64EncodedString(IDictionary 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 ITempDataProvider.LoadTempData(ControllerContext controllerContext)
        {
            return LoadTempData(controllerContext);
        }
    
        void ITempDataProvider.SaveTempData(ControllerContext controllerContext, IDictionary values)
        {
            SaveTempData(controllerContext, values);
        }
    }
    

提交回复
热议问题