2010-09-28 15 views
5

Sto cercando di utilizzare un costume ITempDataProvider fornitore per archiviare TempData in cookie di un browser, invece di stato della sessione. Tuttavia, tutto funziona correttamente, tranne che non riesco a rimuovere il cookie dal flusso di risposta dopo averlo letto.ASP.NET MVC TempData nel cookie del browser

Qualche idea?
Grazie!

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

Fare attenzione a ciò che si memorizza nel cookie del cliente. In genere è una cattiva idea tenere lì le cose. Non sto cercando di giudicare, ma sta facendo questo genere di cose che hanno portato [DotNetNuke a soffrire male] (http://www.youtube.com/watch?v=yghiC_U2RaM) dal recente [oracle padding exploit] (http: // weblogs.asp.net/scottgu/archive/2010/09/18/important-asp-net-security-vulnerability.aspx). –

+0

Accetto con @cottsak. Inoltre, viene inviato con * ogni richiesta. * Ogni immagine. Ogni script Ogni ... –

+0

@cottsak e @Craig: Sto tentando di memorizzare solo notifiche di visualizzazione come "il tuo messaggio è stato inviato". Non sto memorizzando alcun dato sensibile lì. –

risposta

3

Ciao anch'io avuto lo stesso problema ed è stato un problema con l'implementazione di CookieTempDataProvider.

Così ho modificato il codice un po 'e ora funziona perfettamente.

Quando legge i dati dal cookie, lo rimuove sia dalla richiesta che dalla risposta. Ma aggiungi un altro cookie con un valore vuoto nella funzione SaveData che viene chiamata quando l'elaborazione della richiesta è completata.

Punti da notare: se si desidera rimuovere un cookie, è necessario impostare il valore di timeout e inviarlo al client, quindi il browser lo rimuoverà. Non possiamo farlo diversamente dal codice a il cookie è gestito dal browser

E ho scoperto che l'impostazione della scadenza su DateTime.MinValue non scade il cookie in chrome (non so sugli altri browser) così mi sono messo a 2001-01-01 :)

Ecco il codice di lavoro

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

Ecco un esempio di una soluzione di lavoro, senza un sacco di codice in eccesso. Utilizza Json.NET per la serializzazione, che è più veloce di BinaryFormatter + Base64Encoding e produce anche una stringa più breve di (= meno overhead 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); 
     } 
    } 
} 
+0

dovresti inserire alcuni trycatch attorno alla tua deserializzazione –

Problemi correlati