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