ASP.NET MVC TempData in cookie browser

I am trying to use the specialized provider ITempDataProvider to store TempData strong> in a browser cookie instead of session state. However, everything works fine, except that I cannot remove the cookie from the Response stream after reading it.

Any ideas?
Thank you

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); } } 
+5
cookies asp.net-mvc-2
source share
3 answers

Hi, I had the same problem too, and it was a problem with the implementation of CookieTempDataProvider.

So, I changed the code a bit and now it works fine.

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

Note. If you want to delete the cookie, you must set the timeout value and send it to the client, and then the browser will delete it. We cannot do this otherwise than code whose handler is processed by the browser

And I found out that setting the expiration of DateTime.MinValue does not expire from the cookie in chrome (I don't know about other browsers), so I installed it in 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); } } 
+3
source share

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

https://github.com/brockallen/CookieTempData

Here is a link to the blog:

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

He also has a good technique using IControllerFactory so that each controller is supplied with an instance of ITempDataProvider.

+4
source share

Here is an example of a working solution without a lot of extra code. It uses Json.NET to serialize, which is faster than BinaryFormatter + Base64Encoding, and also creates a shorter string significantly (= less HTTP resources).

 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); } } } 
+2
source share

All Articles