Search code examples
c#asp.net-corecookiesasp.net-core-mvcasp.net-core-5.0

How to read response cookies


ASP.NET 5 MVC application method sets HttpContext.Response cookie. How to read this cookie value in other method called from contoller via long call chain in same request ?

Such method does not exist in response collection interface

public interface IResponseCookies
{
    void Append(string key, string value);
    void Append(string key, string value, CookieOptions options);
    void Delete(string key);
    void Delete(string key, CookieOptions options);
}

Current request TempData values set in other methods can read. Why cookies cannot ? Should cookie settings duplicated in HttpContext.Items or is there better method ?

Background:

Shopping cart application has log method called from controllers.

It must log cartid

If user adds product first time to cart, controller creates cart id using new guid and adds cartid cookie to response.

logger method uses Request.Cookies["cartid"]  to log cart it.

For first item added to cart it return null since cookie is not set to browser. 

Response.Cookies["cartid"]

 does not exist.

Log method can called from many places. It is difficult to pass cartid as parameter to it.

Application has log method called from controllers.  It logs controller context to same database used by controllers.

Logging is performed in Error controller created using ASP.NET Core application template:

public async Task<IActionResult> Error()
{
    var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
    await logger.LogExceptionPage(exceptionHandlerPathFeature);
    HttpContext.Response.StatusCode = 500;
    return new ContentResult() {
        Content ="error"
    };
}

How to log Response cookie in this method et by code executed before error ?

Code which causes compile error:

public class CartController : ControllerBase
{
    const string cartid = "cartid";
    private readonly HttpContextAccessor ca;

    public CartController(HttpContextAccessor ca)
    {
        this.ca = ca;
    }

    public IActionResult AddToCartTest(int quantity, string product)
    {
        ca.HttpContext.Response.Cookies.Append(cartid, Guid.NewGuid().ToString());
        Log("AddToCartStarted");
        return View();
    }

    void Log(string activity)
    {
        Console.WriteLine($"{activity} in cart {ca.HttpContext.Response.Cookies[cartid]}");
    }
}

Solution

  • I don't know for sure about ASP.NET 5, but i did read response cookies in net6, so... i hope i can provide an accurate answer.

    Response cookies work a little different than request cookies. While the request cookies are basically converted to a dictionary kind of structure, the response cookies are directly written to the headers. So in order to read the response cookies, you will need to analyse these headers.

    I used the following extension method in the past:

    /// <summary>Extracts the partial cookie value from the header section.</summary>
    /// <param name="headers"><inheritdoc cref="IHeaderDictionary" path="/summary"/></param>
    /// <param name="key">The key for identifying the cookie.</param>
    /// <returns>The value of the cookie.</returns>
    public static string FindCookie(this IHeaderDictionary headers, string key)
    {
       string headerKey = $"{key}=";
       var cookies = headers.Values
         .SelectMany(h => h)
         .Where(header => header.StartsWith(headerKey))
         .Select(header => header.Substring(headerKey.Length).Split(';').First())
         .ToArray();
    
       //Note: cookie values in a header are encoded like a uri parameter value.
       var value = cookies.LastOrDefault();//and the last set value, is the relevant one.
       if (string.IsNullOrEmpty(value))
         return null;
    
       //Thats why we should decode that last value, before we return it.
       var decoded = HttpUtility.UrlDecode(value);
       return decoded; 
    }
    

    How to use:

    var responseCookie = ca.HttpContext.Response.Headers.FindCookie(cartid); 
    

    However there are some things about this method, that i would like to mention first... When cookies are deleted in a response, their expiry is basically set to some point in the past. Thats how browsers notice, that they should remove these cookies. I have no idea how this method would react in such situations. Same goes for cookie meta information like the HttpOnly flag or the SameSite mode.