问题
I am trying to use TempData to send data from one request to another.
However, since TempData uses a server session, and this application is to be web-farmed, I will have to either use cookies or database persistance, instead, since the session won't transfer from one server to the other.
There are a number of implementations available to use cookies instead of the default session:
public class CookieTempDataProvider : ITempDataProvider
{
const string CookieName = "TempData";
public void SaveTempData(
ControllerContext controllerContext,
IDictionary<string, object> values)
{
// convert the temp data dictionary into json
string value = Serialize(values);
// compress the json (it really helps)
var bytes = Compress(value);
// sign and encrypt the data via the asp.net machine key
value = Protect(bytes);
// issue the cookie
IssueCookie(controllerContext, value);
}
public IDictionary<string, object> LoadTempData(
ControllerContext controllerContext)
{
// get the cookie
var value = GetCookieValue(controllerContext);
// verify and decrypt the value via the asp.net machine key
var bytes = Unprotect(value);
// decompress to json
value = Decompress(bytes);
// convert the json back to a dictionary
return Deserialize(value);
}
...
Ref. http://brockallen.com/2012/06/11/cookie-based-tempdata-provider/
However, none of these methods seem to remove the cookie after the request has ended.
Isn't the whole point of using TempData to expire the data after the request has finished (unless you use TempData.Keep("myKey");
)?
Why not just use cookies instead of implementing ITempDataProvider? What's the difference/benefit?
Further reading:
Here is a simpler cookie-based implementation:
- http://vijayt.com/Post/Custom-TempDataProvider-for-Azure
Here is Microsoft's implementation of SessionState provider:
- https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/SessionStateTempDataProvider.cs
Edit for clarification: In the following code, Microsoft is removing the session after it is loaded such that it cannot be loaded again:
// If we got it from Session, remove it so that no other request gets it
session.Remove(TempDataSessionStateKey);
回答1:
None of the solutions I found on the web expired the cookie; so, basically they weren't actually temporary data. They all just let the cookie survive past the LoadTempData() so at that point, you may as well not even be using TempData at all.
In the following implementation, the cookie will only survive for the duration of the HTTP request (as TempData is only supposed to) and if you want to keep it longer, you can use TempData.Keep("yourKey")
as you normally would which will call the SaveTempData() method again (as per the MVC source).
One last thing, this code is NOT optimized for speed or security.
public class CookieTempDataProvider : ITempDataProvider
{
public const string TempDataCookieKey = "__ControllerTempData";
public IDictionary<string, object> LoadTempData(ControllerContext controller)
{
HttpCookie cookie = controller.HttpContext.Request.Cookies[TempDataCookieKey];
Dictionary<string, object> tempDataDictionary = new Dictionary<string, object>();
if (cookie != null)
{
for (int keyIndex = 0; keyIndex < cookie.Values.Count; keyIndex++)
{
string key = cookie.Values.GetKey(keyIndex);
if (!string.IsNullOrEmpty(key))
{
string base64Value = cookie.Values.Get(keyIndex);
byte[] buffer = Convert.FromBase64String(base64Value);
using (MemoryStream ms = new MemoryStream(buffer))
{
BinaryFormatter formatter = new BinaryFormatter();
object value = formatter.Deserialize(ms);
tempDataDictionary.Add(key, value);
}
}
}
cookie.Expires = DateTime.Now.AddDays(-1d); // expire cookie so no other request gets it
controller.HttpContext.Response.SetCookie(cookie);
}
return tempDataDictionary;
}
public void SaveTempData(ControllerContext controller, IDictionary<string, object> values)
{
HttpCookie cookie = controller.HttpContext.Request.Cookies[TempDataCookieKey];
bool hasValues = (values != null && values.Count > 0);
if (cookie == null)
{
cookie = new HttpCookie(TempDataCookieKey);
controller.HttpContext.Response.Cookies.Add(cookie);
}
if (hasValues)
{
foreach (KeyValuePair<string, object> kvp in values)
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
formatter.Serialize(ms, kvp.Value);
byte[] bytes = ms.GetBuffer();
string base64Value = Convert.ToBase64String(bytes);
string keyExists = cookie.Values.Get(kvp.Key);
if (keyExists != null)
{
cookie.Values.Set(kvp.Key, base64Value);
}
else
{
cookie.Values.Add(kvp.Key, base64Value);
}
}
}
cookie.Expires = DateTime.Now.AddDays(1d);
controller.HttpContext.Response.SetCookie(cookie);
}
else
{
// delete session if null values are passed
if (controller.HttpContext.Request.Cookies[TempDataCookieKey] != null)
{
cookie.Expires = DateTime.Now.AddDays(-1d); // expire cookie so no other request gets it
}
}
}
}
来源:https://stackoverflow.com/questions/28351198/implementing-itempdataprovider-vs-using-cookies