【问题标题】:How can I check for a response cookie in Asp.net Core MVC (aka Asp.Net 5 RC1)?如何在 Asp.net Core MVC(又名 Asp.Net 5 RC1)中检查响应 cookie?
【发布时间】:2016-04-27 20:07:06
【问题描述】:

我正在将 Web 表单应用程序转换为 asp.net core mvc。在我的 Web 表单应用程序中,有时在我设置了一些响应 cookie 之后,其他代码需要查看它们是否已设置,如果是,请访问 cookie 的属性(即值、过期、安全、http)。在 webforms 和 MVC 5 中,可以迭代 cookie 并返回任何特定的 cookie(我知道的老派)

       for(int i = 0; i < cookies.Count; i++) {
            if(cookies[i].Name == cookieName) {
                return cookies[i];
            }
        }

但是asp.net core mvc中访问响应cookie的界面是这样的:

基于此界面,我看不到一种方法来检查响应 cookie 是否存在并获取它的属性。但是一定有办法做到吗?

在一个操作方法中,我尝试在响应对象上设置两个 cookie,然后立即尝试访问它们。但是智能感知没有显示任何允许我访问它们的方法、属性或索引器:

有一瞬间,我想也许我可以使用Response.Cookies.ToString(); 并解析信息以找到我的cookie 信息,但是,ToString() 调用返回“Microsoft.AspNet.Http.Internal.ResponseCookies”,因为对象不会覆盖默认实现。

只是为了好玩,我还检查了 GitHub 的当前 dev 分支,看看接口自 RC1 以来是否发生了变化,但实际上没有。那么给定这个接口,我如何检查响应cookie的存在并获取它的属性?我曾考虑尝试通过响应标头集合进行入侵,但这似乎很蹩脚。

【问题讨论】:

  • 你有没有在你的action方法中尝试过Response.Cookies
  • 我会。我将更新问题以表明这一点。
  • 哦,对不起。我的意思是Request.Cookies,我的错!
  • @Will 不用担心。根据您的建议,我尝试将 cookie 添加到响应 cookie,然后检查请求 cookie 以查看是否可以通过这种方式访问​​ cookie。虽然请求 cookie 对象确实提供了一种读取其 cookie 的方法,但事实证明,将 cookie 添加到响应 cookie 不会 像在 asp 中那样将 cookie 添加到引擎盖下的请求 cookie。净 4.x。所以这种方法也不起作用。

标签: asp.net-core-mvc


【解决方案1】:

Microsoft.AspNetCore.Http.Extensions 中有一个扩展方法可用,称为GetTypedHeaders()。这可以在HttpContext.Response 上调用以读取Set-Cookie 标头。例如在中间件中,也许我们想截取一个Set-Cookie 响应头并替换它:

  public async Task Invoke(HttpContext httpContext)
        {
            httpContext.Response.OnStarting(state =>
            {
                var context = (HttpContext)state;

                var setCookieHeaders = context.Response.GetTypedHeaders().SetCookie;

                // We assume only one cookie is found. You could loop over multiple matches instead.
                // setCookieHeaders may be null if response doesn't contain set-cookie headers
                var cookie = setCookieHeaders?.FirstOrDefault(x => x.Name == "mycookiename");

                if (cookie == null)
                {
                    return Task.CompletedTask;
                }

                var opts = new CookieOptions
                {
                    HttpOnly = true,
                    Expires = DateTimeOffset.Now.AddHours(12),
                    SameSite = SameSiteMode.Lax,
                    Secure = true
                };

                context.Response.Cookies.Delete(cookie.Name.Value);
                context.Response.Cookies.Append(cookie.Name.Value, "mynewcookievalue", opts);
                
                return Task.CompletedTask;

            }, httpContext);

            await _next(httpContext);
        }

【讨论】:

  • 不错。这似乎是访问响应 cookie 的更好的现代方式。
【解决方案2】:

以下是我从响应中获取 cookie 的value 的方法。如果需要,可以使用这样的东西来获取整个 cookie:

string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
  foreach (var headers in response.Headers.Values)
    foreach (var header in headers)
      if (header.StartsWith($"{cookieName}="))
      {
        var p1 = header.IndexOf('=');
        var p2 = header.IndexOf(';');
        return header.Substring(p1 + 1, p2 - p1 - 1);
      }
  return null;
}

【讨论】:

    【解决方案3】:

    Shaun 的回答有一个明显的问题:它将匹配任何具有匹配值的 HTTP 标头。这个想法应该是只匹配 cookies

    这样的小改动应该可以解决问题:

    string GetCookieValueFromResponse(HttpResponse response, string cookieName)
    {
      foreach (var headers in response.Headers)
      {
        if (headers.Key != "Set-Cookie")
          continue;
        string header = headers.Value;
        if (header.StartsWith($"{cookieName}="))
        {
          var p1 = header.IndexOf('=');
          var p2 = header.IndexOf(';');
          return header.Substring(p1 + 1, p2 - p1 - 1);
        }
      }
      return null;
    }
    

    现在对 cookie 名称的检查将只针对实际的 cookie-headers。

    【讨论】:

    • 有趣的收获。我现在也看到 Shaun 在循环中使用循环,我也认为这是不正确的。我想我只是从他的回答中得到了需要循环的价值,我最终做到了。我检查了我的代码并检查了header.Key == "Set-Cookie",我认为这可能是正确的,而不是Cookie
    • 我的错,我仔细查看了我的代码,需要两个循环。
    • Ron 的评论是正确的。回复是Set-CookieCookie 应要求提供。
    • 这将获得响应中的第一个 cookie 分配。如果 cookie 设置了两次,第二次会覆盖第一次,而客户端将有效地获得第二次值?
    • @cat_in_hat 绝对正确。我的代码会选择第一个实例,任何符合 RFC 的客户端都会选择最后一个值。
    【解决方案4】:

    您可以使用此功能获取有关 cookie 设置值的完整信息

    private SetCookieHeaderValue GetCookieValueFromResponse(HttpResponse response, string cookieName)
    {
            var cookieSetHeader = response.GetTypedHeaders().SetCookie;
            if (cookieSetHeader != null)
            {
                var setCookie = cookieSetHeader.FirstOrDefault(x => x.Name == cookieName);
                return setCookie;
            }
            return null;
    }
    

    【讨论】:

      【解决方案5】:

      在我的情况下,我只需要获取 auth= 的值,而不关心其他值,但是您可以轻松地重写代码以获取 cookie 值的字典。

      我为 HttpResponseMessage 编写了这个扩展,为了避免使用索引和子字符串,我将字符串分离为数组,然后将其转换为字典

          internal static string GetCookieValueFromResponse(this HttpResponseMessage httpResponse, string cookieName)
          {
      
              foreach (var cookieStr in httpResponse.Headers.GetValues("Set-Cookie"))
              {
                  if(string.IsNullOrEmpty(cookieStr))
                      continue;
      
                  var array = cookieStr.Split(';')
                      .Where(x => x.Contains('=')).Select(x => x.Trim());
      
                  var dict = array.Select(item => item.Split(new[] {'='}, 2)).ToDictionary(s => s[0], s => s[1]);
      
      
                  if (dict.ContainsKey(cookieName))
                      return dict[cookieName];
              }
      
              return null;
          }
      

      【讨论】:

        【解决方案6】:

        您可以使用此方法从响应中获取 cookie 字符串值。在这种情况下,我在 CookieService 中使用此逻辑,这就是您会看到我使用 _contextAccessor 的原因。请注意,在这种情况下,如果 cookie 的值为空,我将返回 null。 (这是可选的)

            private string GetCookieFromResponse(string cookieName)
            {
                var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
                var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
                var cookieValue = cookie?.Value.ToString();
                if (!string.IsNullOrEmpty(cookieValue))
                {
                    cookieValue = Uri.UnescapeDataString(cookieValue);
                }
        
                return cookieValue;
            }
        

        如果您正在寻找一种从响应或请求中获取 cookie 的解决方案,那么您可以使用它。

            private string GetCookieString(string cookieName)
            {
                return GetCartCookieFromResponse(cookieName) ?? GetCartCookieFromRequest(cookieName) ?? "";
            }
        
            private string GetCookieFromRequest(string cookieName)
            {
                return _contextAccessor.HttpContext.Request.Cookies.ContainsKey(cookieName) ? _contextAccessor.HttpContext.Request.Cookies[cookieName] : null;
            }
        
            private string GetCookieFromResponse(string cookieName)
            {
                var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
                var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
                var cookieValue = cookie?.Value.ToString();
                if (!string.IsNullOrEmpty(cookieValue))
                {
                    cookieValue = Uri.UnescapeDataString(cookieValue);
                }
        
                return cookieValue;
            }
        

        【讨论】:

        • 这看起来很不错,只是如果 cookie 有一个空白值,它将返回 null 而不是空白字符串。这可以通过删除 &amp;&amp; !string.IsNullOrEmpty(x.Value.ToString() 来解决
        • 嗨@RonC,你是对的,我添加了验证,因为我想确保 cookie 有价值。如果没有,我会在请求中查找 cookie。在这种情况下打算返回 null。我更新了包括该逻辑的答案。感谢您的反馈。
        【解决方案7】:

        sn-p 似乎对我有用。我的结果(来自 Shaun 和 Ron)看起来像:

                public static string GetCookieValueFromResponse(HttpResponse response, string cookieName)
                {
                    // inspect the cookies in HttpResponse.
        
                    string match = $"{cookieName}=";
                    var p1 = match.Length;
        
                    foreach (var headers in response.Headers)
                    {
                        if (headers.Key != "Set-Cookie")
                            continue;
                        foreach (string header in headers.Value)
                        {
                            if (header.StartsWith(match) && header.Length > p1 && header[p1] != ';')
                            {
                                var p2 = header.IndexOf(';', p1);
                                return header.Substring(p1 + 1, p2 - p1 - 1);
                            }
                        }
                    }
                    return null;
                }
        

        【讨论】:

          【解决方案8】:

          如果存在多个“Set-Cookie”标头,则应使用最后一个:

              private string GetCookieValueFromResponse(Microsoft.AspNetCore.Http.HttpResponse response, string cookieName)
              {
          
                  var value = string.Empty;
          
                  foreach (var header in response.Headers["Set-Cookie"])
                  {
                      if (!header.Trim().StartsWith($"{cookieName}="))
                          continue;
          
                      var p1 = header.IndexOf('=');
                      var p2 = header.IndexOf(';');
          
                      value = header.Substring(p1 + 1, p2 - p1 - 1);
          
                  }
          
                  return value;
              }
          

          【讨论】:

          • 你说得对。但是,您的代码中有一个小问题。它没有检查 Set-Cookie 标头是否存在这一事实。肖恩和我之前的尝试解释了这一点。小修小补,完美!
          猜你喜欢
          • 1970-01-01
          • 2016-09-20
          • 1970-01-01
          • 2020-12-13
          • 2021-06-28
          • 2020-01-25
          • 2016-04-24
          • 2022-01-04
          • 1970-01-01
          相关资源
          最近更新 更多