У меня проблемы с кодом управления файлом cookie. На каждой странице я смотрю, установлено ли значение ключа. Моя проблема в том, что просмотр файла cookie создает пустой файл cookie.HttpCookie работает локально, но не на сервере
Локально, я могу посмотреть на файл cookie, а затем установить значение ключа, и все в порядке с миром. Но когда я переношу свой код на тестовый сервер, все ведет себя по-другому. Для одного, локально, выдается только один cookie (показано в хром-инструментах). На сервере выдается 2 файла cookie с тем же кодом.
Я написал этот код около 4 лет назад, и он был разработан для .net 2.0, когда поведение HttpCookie было изменено, так что просмотр cookie создавал пустой, если он не существовал. Раньше в .Net 1.1 возвращался null. Теперь мне интересно, что что-то фундаментальное изменилось между 2.0 и 4.0.
Я должен отметить, что мой локальный компьютер - это Windows 7, а сервер - это сервер Windows 2003, но это не должно иметь никакого значения. Единственное, что нужно отметить, это то, что приложение работает в виртуальном каталоге на сервере. Может ли это сыграть роль в проблемах? Я не уверен. Я попытался установить путь к файлу cookie на путь виртуального каталога без везения.
My cookie имеет несколько уровней, включая базовый уровень для непосредственного взаимодействия с http cookie, уровень шифрования, который можно включить или отключить для отладки, а затем уровень управления. Я собираюсь поделиться своим кодом в надежде, что у меня есть вопиющая ошибка в моей логике.
Я также хочу упомянуть, что я использую cookie FormsAuthentication для чего-то другого. Этот файл cookie предназначен для некоторых посторонних, но требуемых данных.
Мой базовый слой печенья:
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);
}
}
Мой шифрования слоя (батареи не включены)
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);
}
}
}
И, наконец, слой управления печенья, который оборачивает все это.
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);
}
}
Существует также настраиваемая конфигурация, которая помогает настроить файлы cookie. Вот как это выглядит:
<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>
Теперь мой код выглядит примерно так в моем веб-приложение:
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();
}
Я называю свойство первым, чтобы увидеть, если идентификатор пользователя был установлен. Этот вызов выполняется на каждой странице. Затем на другой странице пользователь может аутентифицироваться, и я установил идентификатор в файл cookie. Все работает локально, но если я попал на главную страницу на сервере (где id выполняет поиск по id), а затем аутентифицируется, значение cookie не задано. Он остается пустым.
Win 7 и Win Server 2003 очень разные - Win 7 имеет IIS7, а сервер использует IIS6. Вы запускаете это в Cassini (сервер Visual Studio) или в локальном IIS? – davisoa
На сервере с установленными двумя файлами cookie и конфигурацией, указанными выше, один из них задан как «путь», а другой - «/ MyVirtualDir» в качестве пути. Настройка пути к файлу cookie не изменила этого. – Josh
@ davisoa - Да, Cassini на локальном и IIS6 на сервере. Однако код был разработан для .net 2.0, а библиотека - обновлена до 4.0. – Josh