ASP.NET MVC TempData in browser cookie

后端 未结 3 669
醉梦人生
醉梦人生 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 08:57

    Here is an example of a working solution without lots of excess code. It uses Json.NET for serializing, which is faster than BinaryFormatter + Base64Encoding and also produces a much shorter string (=less http overhead).

    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);
            }
        }
    }
    
    0 讨论(0)
  • 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<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);
        }
    }
    
    0 讨论(0)
  • 2021-01-13 09:10

    There is a better solution by Brock Allen on GitHub that uses encryption, 2 forms of serialization, and compression to protect and optimize the cookies.

    https://github.com/brockallen/CookieTempData

    Here is a link to the blog about it:

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

    He also has a good technique using IControllerFactory to ensure every controller is supplied with an instance of ITempDataProvider.

    0 讨论(0)
提交回复
热议问题