Why does HttpCookieCollection.Get returns null when called from a manually instantiated object (and not from the current HttpContext)

StackOverflow https://stackoverflow.com/questions/16401343

Question

In the HttpCookieCollection.Get MSDN documentation, it is stated that:

If the named cookie does not exist, this method creates a new cookie with that name.

This is true and works well when calling HttpContext.Request.Cookies or HttpContext.Response.Cookies from a "real" web server.

However, this code:

    HttpCookieCollection foo = new HttpCookieCollection();
    HttpCookie cookie = foo.Get("foo");
    Console.WriteLine(cookie != null);

Displays False (cookie is null).

This is not the case if the HttpCookieCollection is retrieved from Request.Cookies in a HTTP handler.

Any idea of what is wrong here/if any other setup is needed?

I'm asking this because I write unit tests where I mock HttpContextBase, so no "real" context is provided.

Thank you for your help

Was it helpful?

Solution

If you look at the code for HttpCookieCollection.Get(string), you'll see something like this:

public HttpCookie Get(string name)
{
  HttpCookie cookie = (HttpCookie) this.BaseGet(name);
  if (cookie == null && this._response != null)
  {
    cookie = new HttpCookie(name);
    this.AddCookie(cookie, true);
    this._response.OnCookieAdd(cookie);
  }
  if (cookie != null)
    this.EnsureKeyValidated(name, cookie.Value);
  return cookie;
}

It's never creating the cookie because _response is going to be null (look at the first 'if' statement). i.e. there's no response object to send the new cookie back to, so it won't create it.

The response object is an HttpResponse object and it's passed through to the internal constructor (so that constructor is not available to you).

I personally never liked the way the Get method acts on HttpCookieCollection; it violates the Command-Query separation principle: asking a question should not change the answer.

I would recommend you check for the existence of the cookie by checking the AllKeys property; if it doesn't exist, explicitly create and add the cookie to the collection. Otherwise, if you know the key exists, go ahead and fetch the existing entry. Then your production code and unit tests should behave.

It might be a good idea to create a helper or extension method to use instead of Get, to ensure it behaves as you expect whether you're unit testing or running normally:

public static class HttpCookieCollectionExtensions
{
    public static HttpCookie GetOrCreateCookie(this HttpCookieCollection collection, string name)
    {
        // Check if the key exists in the cookie collection. We check manually so that the Get
        // method doesn't implicitly add the cookie for us if it's not found.
        var keyExists = collection.AllKeys.Any(key => string.Equals(name, key, StringComparison.OrdinalIgnoreCase));

        if (keyExists) return collection.Get(name);

        // The cookie doesn't exist, so add it to the collection.
        var cookie = new HttpCookie(name);
        collection.Add(cookie);
        return cookie;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top