浏览器cookie中的ASP.NET MVC TempData

时间:2010-09-28 02:59:18

标签: asp.net-mvc-2 cookies tempdata

我正在尝试使用自定义 ITempDataProvider 提供程序将 TempData 存储在浏览器的Cookie中而不是会话状态中。但是,一切正常,但我无法在读取后从Response流中删除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 个答案:

答案 0 :(得分:4)

Brock Allen在GitHub上有一个更好的解决方案,它使用加密,2种形式的序列化和压缩来保护和优化cookie。

https://github.com/brockallen/CookieTempData

以下是关于它的博客链接:

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

他还有一个很好的技术,使用IControllerFactory来确保为每个控制器提供一个ITempDataProvider实例。

答案 1 :(得分:3)

您好我也有同样的问题,这是CookieTempDataProvider实现的问题。

所以我稍微修改了一下代码,现在它完美无缺。

当它从cookie中读取数据时,它会从请求和响应中删除它。但是在SaveData函数中添加另一个空值的cookie,该函数在请求处理完成时调用。

注意事项:如果要删除cookie,则必须设置超时值并将其发送回客户端,然后浏览器将其删除。我们无法通过浏览器处理cookie来代码执行此操作

我发现将DateTime.MinValue的到期设置不会使chrome中的cookie失效(不知道其他浏览器)所以我将其设置为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 :(得分:2)

以下是没有大量代码的工作解决方案的示例。它使用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);
        }
    }
}