Asp.net core mvc 如何在Asp.net核心MVC(又名Asp.net 5 RC1)中检查响应cookie?

Asp.net core mvc 如何在Asp.net核心MVC(又名Asp.net 5 RC1)中检查响应cookie?,asp.net-core-mvc,Asp.net Core Mvc,我正在将web窗体应用程序转换为asp.net核心mvc。在我的web窗体应用程序中,有时在我设置了一些响应cookie之后,其他代码需要查看它们是否已设置,如果已设置,则需要访问cookie的属性(即值、过期、安全、http)。在webforms和MVC5中,可以迭代cookies并返回任何特定的cookies,就像这样(老派我知道) for(int i=0;i

我正在将web窗体应用程序转换为asp.net核心mvc。在我的web窗体应用程序中,有时在我设置了一些响应cookie之后,其他代码需要查看它们是否已设置,如果已设置,则需要访问cookie的属性(即值、过期、安全、http)。在webforms和MVC5中,可以迭代cookies并返回任何特定的cookies,就像这样(老派我知道)

for(int i=0;i
但是asp.net core mvc中用于访问响应cookie的接口如下所示:

基于此接口,我看不到检查响应cookie是否存在并获取其属性的方法。但一定有办法做到这一点

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

有那么一会儿,我想也许我可以使用
Response.Cookies.ToString()
并只解析信息以查找我的cookie信息,但是遗憾的是,
ToString()
调用返回“Microsoft.AspNet.Http.Internal.ResponseCookies”,因为该对象没有覆盖默认实现


只是为了好玩,我还检查了GitHub的当前开发分支,查看自RC1以来接口是否发生了更改,但没有。因此,给定这个接口,我如何检查响应cookie的存在并获取其属性?我曾想过尝试通过response headers集合黑客入侵,但这似乎很蹩脚。

以下是我如何从响应中获取cookie的
值的方法。如果需要,可以使用类似这样的方法来获取整个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;
}

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头。

这个片段似乎适合我。我的结果(来自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;
        }
如果存在多个“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;
    }

在我的例子中,我必须只获取auth=的值,而不关心其他值,但是您可以轻松地重写代码以获取cookie值的dict

我为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;
    }

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集值的完整信息

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;
}

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

如果您正在寻找一种解决方案,该解决方案按照响应或请求的顺序获取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;
    }

您是否尝试过在您的操作方法中使用
Response.Cookies
。我会更新问题来说明这一点。哦,对不起。我的意思是请求。Cookies,我的错@我不会担心的。根据您的建议,我尝试将cookie添加到响应cookie中,然后检查请求cookie,看看是否可以通过这种方式访问cookie。虽然request cookies对象确实提供了一种读取其cookie的方法,但事实证明,将cookie添加到响应cookie中并不会像asp.net 4.x中那样将cookie添加到引擎盖下的请求cookie中。所以这种方法也不起作用。有趣的捕获。我现在也看到Shaun在循环中使用了一个循环,我也认为这是不对的。我猜我只是从他的回答中得出,价值需要循环,我最终通过了这个循环。我检查了我的代码,我检查了
header.Key==“Set Cookie”
,我认为这可能是正确的,而不是
Cookie
我的错误,我仔细查看了我的代码,需要两个循环。Ron的评论是正确的。它是
在响应时设置Cookie
Cookie
应请求。这将获得响应中的第一个Cookie分配。如果cookie设置两次,第二次将覆盖第一次,客户端将有效地拥有第二个值?@cat_in_hat绝对正确。我的代码将选择第一个实例,任何符合RFC的客户机都将选择最后一个值。这一点你是绝对正确的。但是,代码中有一个小问题。它没有检查
Set Cookie
头是否存在。肖恩和我之前的尝试就说明了这一点。小补丁,很完美!美好的这似乎是访问响应cookie的更好的现代方式。这看起来很不错,只是如果cookie有一个空值,它将返回null而不是一个空字符串。这可以通过删除
&>来解决!string.IsNullOrEmpty(x.Value.ToString()
Hi@RonC,你是对的,我添加了验证,因为我想确保cookie有值。如果没有,我会在请求中查找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;
    }
    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;
    }