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