5

我正在尝试使用自定义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);
        }
    }
4

3 回答 3

4

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

https://github.com/brockallen/CookieTempData

这是有关它的博客的链接:

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

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

于 2013-05-05T10:47:26.283 回答
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);
    }
}
于 2011-03-15T07:02:55.350 回答
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);
        }
    }
}
于 2012-06-03T09:40:25.627 回答