0

J'ai des problèmes avec mon code de gestion des cookies. Sur chaque page, je regarde pour voir si une valeur clé a été définie. Mon problème est que regarder le cookie crée un cookie vide. Localement, je peux regarder le cookie et ensuite définir la valeur de la clé et tout va bien dans le monde. Mais quand je déplace mon code sur le serveur de test, tout se comporte différemment. Pour un, localement, un seul cookie est émis (montré dans les outils de chrome). Sur le serveur, 2 cookies sont émis avec le même code.HttpCookie fonctionne localement mais pas sur le serveur

J'ai écrit ce code il y a environ 4 ans, et il a été conçu pour .net 2.0 lorsque le comportement de HttpCookie a été modifié de sorte que l'examen du cookie en ait créé un vide s'il n'existait pas. Auparavant, dans .Net 1.1 null a été renvoyé. Maintenant, je me demande quelque chose de fondamental a été changé entre 2.0 et 4.0.

Je devrais noter que mon ordinateur local est Windows 7 et le serveur est un serveur Windows 2003 - pas que cela devrait faire une différence. La seule chose à noter est que l'application s'exécute dans un répertoire virtuel sur le serveur. Cela pourrait-il jouer un rôle dans les problèmes? Je ne suis pas sûr. J'ai essayé de définir le chemin du cookie sur le chemin du répertoire virtuel sans aucune chance.

Mon cookie comporte plusieurs couches, dont une couche de base pour traiter directement le cookie http, une couche de chiffrement pouvant être activée ou désactivée pour le débogage, puis une couche de gestion. Je vais partager mon code dans l'espoir d'avoir une erreur flagrante dans ma logique.

Je tiens également à mentionner que j'utilise le cookie FormsAuthentication pour autre chose. Ce cookie est destiné à des données étrangères, mais nécessaires.

Ma couche de biscuit de base:

public abstract class BaseCookie 
{ 
    #region Private Variables 
    private string cookieName; 
    private int timeout = 30; 
    private ExpirationMode expirationMode = ExpirationMode.SlidingExpiration; 
    private string domain; 
    private bool httpOnly = true; 
    private string path = "/"; 
    private bool secure; 
    private string cookieValue; 
    #endregion 

    #region Public Properties 

    /// <summary> 
    /// The name of the cookie as it appears in the request and response 
    /// </summary> 
    protected string CookieName 
    { 
     get { return cookieName; } 
     set { cookieName = value; } 
    } 

    /// <summary> 
    /// The expiration mode of the cookie (default SlidingExpiration) 
    /// </summary> 
    public ExpirationMode ExpirationMode 
    { 
     get { return expirationMode; } 
    } 

    /// <summary> 
    /// The timeout in minutes (default 30) 
    /// </summary> 
    public int Timeout 
    { 
     get { return timeout; } 
     set { timeout = value; } 
    } 

    /// <summary> 
    /// The cookie domain (default String.Empty) 
    /// </summary> 
    public string Domain 
    { 
     get { return domain; } 
     set { domain = value; } 
    } 

    /// <summary> 
    /// Whether or not the cookie is http only (default true) 
    /// </summary> 
    public bool HttpOnly 
    { 
     get { return httpOnly; } 
     set { httpOnly = value; } 
    } 

    /// <summary> 
    /// The path of the cookie (default "/") 
    /// </summary> 
    public string Path 
    { 
     get { return path; } 
     set { path = value; } 
    } 

    /// <summary> 
    /// Whether or not the cookie supports https (default false) 
    /// </summary> 
    public bool Secure 
    { 
     get { return secure; } 
     set { secure = value; } 
    } 

    /// <summary> 
    /// The Value of the cookie (NOTE: this should only be used for setting the value when calling AppendNewCookie(). 
    /// This will not change the value of the cookie after initialization. Use SetCookieValue and GetCookieValue after initialization. 
    /// </summary> 
    public string Value 
    { 
     get { return cookieValue; } 
     set { cookieValue = value; } 
    } 

    /// <summary> 
    /// The cookie in the Request 
    /// </summary> 
    private HttpCookie RequestCookie 
    { 
     get 
     { 
      return HttpContext.Current.Request.Cookies.Get(CookieName); 
     } 
    } 

    /// <summary> 
    /// The cookie in the Response 
    /// </summary> 
    private HttpCookie ResponseCookie 
    { 
     get 
     { 
      return HttpContext.Current.Response.Cookies.Get(CookieName); 
     } 
    } 

    #endregion 

    #region Constructors 
    /// <summary> 
    /// Constructor setting the name of the cookie with Sliding Expiration 
    /// </summary> 
    /// <param name="cookieName">the name of the cookie</param> 
    public BaseCookie(string cookieName) 
     : this(cookieName, ExpirationMode.SlidingExpiration) 
    { 

    } 

    /// <summary> 
    /// Constructor setting the name of the cookie and the expiration mode 
    /// </summary> 
    /// <param name="cookieName">the name of the cookie</param> 
    /// <param name="expirationMode">the Olympus.Cookies.ExpirationMode of the cookie</param> 
    public BaseCookie(string cookieName, ExpirationMode expirationMode) 
    { 
     this.cookieName = cookieName; 

     CookieConfig config = Configuration.CookieConfiguration.Cookies[cookieName]; 

     if (config != null) 
     { 
      Domain = config.Domain; 
      HttpOnly = config.HttpOnly; 
      Path = config.Path; 
      Secure = config.Secure; 
      Timeout = config.Timeout; 
     } 

     //EnsureCookie(); 
    } 
    #endregion 

    /// <summary> 
    /// This method ensures that the cookie is not empty if it exists in either the request or response. 
    /// Due to changes in the cookie model for 2.0, this step is VITAL to cookie management. 
    /// </summary> 
    protected void EnsureCookie() 
    { 
     //if the cookie doesn't exist in the response (hasn't been altered or set yet this postback) 
     if (IsNull(ResponseCookie)) 
     { 
      //if the cookie exists in the request 
      if (!IsNull(RequestCookie)) 
      { 
       //get the cookie from the request 
       HttpCookie cookie = RequestCookie; 

       //update the expiration 
       if (ExpirationMode == ExpirationMode.NoExpiration) 
       { 
        cookie.Expires = DateTime.Now.AddYears(10); 
       } 
       else 
       { 
        cookie.Expires = DateTime.Now.AddMinutes(Timeout); 
       } 

       //set the cookie into the response 
       HttpContext.Current.Response.Cookies.Set(cookie); 
      } 
      else 
      { 
       //if the response and request cookies are null, append a new cookie 
       AppendNewCookie(); 
      } 
     } 
    } 

    /// <summary> 
    /// Append an empty cookie to the Response 
    /// </summary> 
    public virtual void AppendNewCookie() 
    { 
     HttpCookie cookie = new HttpCookie(CookieName); 

     cookie.Domain = Domain; 
     cookie.HttpOnly = HttpOnly; 
     cookie.Path = Path; 
     cookie.Secure = Secure; 

     cookie.Value = Value; 

     if (ExpirationMode == ExpirationMode.NoExpiration) 
     { 
      cookie.Expires = DateTime.Now.AddYears(10); 
     } 
     else 
     { 
      cookie.Expires = DateTime.Now.AddMinutes(Timeout); 
     } 

     HttpContext.Current.Response.Cookies.Add(cookie); 
    } 

    /// <summary> 
    /// Determine if the Cookie is null. 
    /// </summary> 
    /// <remarks> 
    /// Due to changes in the 2.0 cookie model, looking in the request or respnse creates an empty cookie with an 
    /// expiration date of DateTime.MinValue. Previously, a null value was returned. This code calls one of these 
    /// empty cookies a null cookie. 
    /// </remarks> 
    /// <param name="cookie">the cookie to test</param> 
    /// <returns>System.Boolean true if the cookie is "null"</returns> 
    protected static bool IsNull(HttpCookie cookie) 
    { 
     if (cookie == null) 
     { 
      return true; 
     } 
     else 
     { 
      if (String.IsNullOrEmpty(cookie.Value) && cookie.Expires == DateTime.MinValue) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

    /// <summary> 
    /// Update the expiration of the response cookie 
    /// <remarks> 
    /// If this is not done, the cookie will never expire because it will be updated with an expiry of DateTime.Min 
    /// </remarks> 
    /// </summary> 
    protected void SetExpiration() 
    { 
     if (ExpirationMode == ExpirationMode.NoExpiration) 
     { 
      ResponseCookie.Expires = DateTime.Now.AddYears(10); 
     } 
     else 
     { 
      ResponseCookie.Expires = DateTime.Now.AddMinutes(Timeout); 
     } 
    } 

    /// <summary> 
    /// Set the value of a cookie. 
    /// </summary> 
    /// <remarks> 
    /// Setting value will override all attributes. 
    /// </remarks> 
    /// <param name="value">the value to set the cookie</param> 
    public virtual void SetCookieValue(string value) 
    { 
     //EnsureCookie(); 

     ResponseCookie.Value = value; 

     SetExpiration(); 
    } 

    /// <summary> 
    /// Get the default value (the first value) of the cookie 
    /// </summary> 
    /// <remarks> 
    /// Getting the value only returns the first value (values[0]) because it has no key. 
    /// </remarks> 
    /// <returns>System.String</returns> 
    public virtual string GetCookieValue() 
    { 
     //EnsureCookie(); 

     string returnValue = ""; 

     if (RequestCookie.Values.Count > 0) 
     { 
      returnValue = RequestCookie.Values[0]; 
     } 

     return returnValue; 
    } 

    /// <summary> 
    /// Set a key/value pair 
    /// </summary> 
    /// <param name="key">the key for the pair</param> 
    /// <param name="value">the value to assign to the key</param> 
    public virtual void SetKeyValue(string key, string value) 
    { 
     //EnsureCookie(); 

     ResponseCookie.Values.Set(key, value); 

     SetExpiration(); 
    } 

    /// <summary> 
    /// Get a value for a given key 
    /// </summary> 
    /// <param name="key">the key to find a value of</param> 
    /// <returns>System.String</returns> 
    public virtual string GetKeyValue(string key) 
    { 
     //EnsureCookie(); 

     return RequestCookie.Values[key]; 
    } 

    /// <summary> 
    /// Expire the cookie 
    /// </summary> 
    public virtual void Expire() 
    { 
     //Setting the expiration does not remove the cookie from the next request 
     ResponseCookie.Expires = DateTime.Now.AddDays(-1); 
    } 
} 

Ma couche de chiffrement (piles non comprises)

internal sealed class EncryptedCookie : BaseCookie 
{ 
    private string decryptedCookieName; 

    new public string Value 
    { 
     get 
     { 
      if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
      { 
       return Encryption.DecryptQueryString(base.Value); 
      } 
      else 
      { 
       return base.Value; 
      } 
     } 
     set 
     { 
      if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
      { 
       base.Value = Encryption.EncryptQueryString(value); 
      } 
      else 
      { 
       base.Value = value; 
      } 
     } 
    } 

    public EncryptedCookie(string cookieName) 
     : base(cookieName) 
    { 
     decryptedCookieName = cookieName; 

     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      CookieName = Encryption.EncryptQueryString(cookieName); 
     } 

     EnsureCookie(); 
    } 

    public EncryptedCookie(string cookieName, ExpirationMode expirationMode) 
     : base(cookieName, expirationMode) 
    { 
     decryptedCookieName = cookieName; 

     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      CookieName = Encryption.EncryptQueryString(cookieName); 
     } 

     EnsureCookie(); 
    } 

    public override string GetCookieValue() 
    { 
     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      return Encryption.DecryptQueryString(base.GetCookieValue()); 
     } 
     else 
     { 
      return base.GetCookieValue(); 
     } 
    } 

    public override void SetCookieValue(string value) 
    { 
     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      base.SetCookieValue(Encryption.EncryptQueryString(value)); 
     } 
     else 
     { 
      base.SetCookieValue(value); 
     } 
    } 

    public override void SetKeyValue(string key, string value) 
    { 
     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      base.SetKeyValue(Encryption.EncryptQueryString(key), Encryption.EncryptQueryString(value)); 
     } 
     else 
     { 
      base.SetKeyValue(key, value); 
     } 
    } 

    public override string GetKeyValue(string key) 
    { 
     if (Configuration.CookieConfiguration.Cookies[decryptedCookieName].EnableEncryption) 
     { 
      return Encryption.DecryptQueryString(base.GetKeyValue(Encryption.EncryptQueryString(key))); 
     } 
     else 
     { 
      return base.GetKeyValue(key); 
     } 
    } 
} 

Et enfin, la couche de gestion des cookies qui l'enveloppe tout.

public sealed class Cookie 
{ 
    string cookieName; 

    public Cookie(string cookieName) 
    { 
     this.cookieName = cookieName; 
    } 

    public void AppendCookie() 
    { 
     EncryptedCookie cookie = new EncryptedCookie(cookieName); 

     cookie.AppendNewCookie(); 
    } 

    public void SetAttribute(string attributeName, string attributeValue) 
    { 
     EncryptedCookie cookie = new EncryptedCookie(cookieName); 

     cookie.SetKeyValue(attributeName, attributeValue); 
    } 

    public void SetValue(string value) 
    { 
     EncryptedCookie cookie = new EncryptedCookie(cookieName); 

     cookie.SetCookieValue(value); 
    } 

    public string GetValue() 
    { 
     EncryptedCookie cookie = new EncryptedCookie(cookieName); 

     return cookie.GetCookieValue(); 
    } 

    public string GetAttribute(string attributeName) 
    { 
     EncryptedCookie cookie = new EncryptedCookie(cookieName); 

     return cookie.GetKeyValue(attributeName); 
    } 
} 

Il existe une configuration personnalisée avec ceci qui aide à configurer le (s) cookie (s). Voilà ce qui ressemble à:

<cookie> 
     <cookies> 
      <!-- enableEncryption: [ true | false ] --> 
      <!-- expirationMode: [ SlidingExpiration | NoExpiration ] --> 
      <!-- timeout: the timeout in minutes (Default 30)--> 
      <!-- httpOnly: [ true | false ] --> 
      <!-- secure: [ true | false ] --> 
      <add name="MyCookie" enableEncryption="true" expirationMode="SlidingExpiration" timeout="64800" domain="" httpOnly="true" path="" secure="false" /> 
     </cookies> 
    </cookie> 

Maintenant, mon code ressemble à quelque chose comme ça dans mon application web:

public int MyId 
    { 
     get 
     { 

      Cookie cookie = new Cookie(ConfigurationHelper.Cookies.MyCookie); 

      int myId= 0; 
      try 
      { 
       Int32.TryParse(cookie.GetAttribute(BILLERID_KEY), out myId); 
      } 
      catch (System.NullReferenceException) 
      { 
       //do nothing on purpose. 
      } 

      return myId; 

     } 
     set 
     { 
      Cookie cookie = new Cookie(ConfigurationHelper.Cookies.MyCookie); 

      cookie.SetAttribute(MY_KEY, value.ToString()); 
     } 
    } 

    public void SetMyId(int myId) 
    { 
     Cookie cookie = new Cookie(ConfigurationHelper.Cookies.MyCookie); 

     cookie.SetAttribute(MYID_KEY, myId.ToString()); 

     cookie.AppendCookie(); 
    } 

J'appelle la propriété d'abord si l'ID de l'utilisateur a été défini. Cet appel est fait sur chaque page. Ensuite, sur une autre page, l'utilisateur peut s'authentifier et mettre l'identifiant dans le cookie. Tout fonctionne localement, mais si je clique sur la page d'accueil du serveur (où id fait la recherche sur l'identifiant), puis s'authentifie, la valeur du cookie n'est pas définie. Il reste vide.

+0

Win 7 et Win Server 2003 sont très différents - Win 7 a IIS7 et le serveur utilise IIS6. Courez-vous ceci dans Cassini (serveur de Visual Studio) ou sur votre IIS local? – davisoa

+0

Sur le serveur, avec les 2 cookies qui sont définis et la configuration ci-dessus, l'un est défini avec "/" comme chemin et l'autre avec "/ MyVirtualDir" comme chemin. Définir le chemin du cookie n'a pas changé cela. – Josh

+0

@davisoa - Oui, Cassini en local et IIS6 sur le serveur. Le code a toutefois été conçu pour .net 2.0 et la bibliothèque est mise à niveau vers la version 4.0. – Josh

Répondre

1

Est-ce le problème que vous n'êtes pas appeler

cookie.AppendCookie(); 

dans l'accesseur set pour MyId? Vous l'appelez dans la méthode SetMyId.

+0

http://msdn.microsoft.com/en-us/library/ms178194.aspx dit que je ne dois pas. Je peux modifier le cookie directement dans la collection. – Josh

+0

Mais vous utilisez la méthode Get pour récupérer le cookie, pas les accesseurs d'élément utilisés dans l'exemple. Je pensais que l'utilisation de Get pour récupérer le cookie signifiait que vous deviez utiliser Set pour mettre à jour le cookie dans la collection de cookies de réponse. –

+0

Le comportement a été très étrange. Ça fonctionnait, puis ça s'est arrêté. Je vais supprimer le code ci-dessus et le faire comme le dit l'article MSDN. – Josh

1

Le cookie est créé lorsque vous le recherchez sur HttpContext.Current.Response. J'ai travaillé autour de ceci en collant une copie du cookie dans HttpContext.Current.Items quand je l'ai placé. Ensuite, quand j'ai besoin de vérifier si elle a été définie ou non, je vérifie les articles au lieu de Response.Cookies.

Ce n'est pas la solution la plus élégante, mais elle fait l'affaire.

+0

Même si le mannequin a été créé lorsque je le regarde, je le retire et j'essaie d'y mettre des valeurs. Ça n'a pas de sens que ça ne me laisse pas faire ça. - Eh bien, il le fait localement, mais pas sur le serveur. – Josh

Questions connexes