문제
CookieContainer가 도메인을 처리하는 방법이 혼란 스러워서이 테스트를 만듭니다. 이 테스트는 CookieContainer가 "example.com"에 대한 쿠키를 반환하지 않지만 RFC에 따르면 최소 2 개의 쿠키를 반환해야합니다.
버그가 아닌가요?
어떻게 작동합니까?
이 버그에 대한 토론은 다음과 같습니다.
http://social.msdn.microsoft.com/forums/en-us/ncl/thread/c4edc965-2dc2-4724-8f08-68815cf1dce6
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Net" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
CookieContainer getContainer()
{
CookieContainer result = new CookieContainer();
Uri uri = new Uri("http://sub.example.com");
string cookieH = @"Test1=val; domain=sub.example.com; path=/";
result.SetCookies(uri, cookieH);
cookieH = @"Test2=val; domain=.example.com; path=/";
result.SetCookies(uri, cookieH);
cookieH = @"Test3=val; domain=example.com; path=/";
result.SetCookies(uri, cookieH);
return result;
}
void Test()
{
CookieContainer cookie = getContainer();
lblResult.Text += "<br>Total cookies count: " + cookie.Count + " expected: 3";
Uri uri = new Uri("http://sub.example.com");
CookieCollection coll = cookie.GetCookies(uri);
lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " expected: 2";
uri = new Uri("http://other.example.com");
coll = cookie.GetCookies(uri);
lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " expected: 2";
uri = new Uri("http://example.com");
coll = cookie.GetCookies(uri);
lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " expected: 2";
}
protected void Page_Load(object sender, EventArgs e)
{
Test();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CookieContainer Test Page</title>
</head>
<body>
<form id="frmTest" runat="server">
<asp:Label ID="lblResult" EnableViewState="false" runat="server"></asp:Label>
</form>
</body>
</html>
해결책
방금이 버그에 대한 수정 사항을 찾아서 여기에서 논의했습니다.http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain handling-bug-fix.html
다음은 해결책입니다.
- .add (쿠키)를 사용하지 말고 .add (uri, 쿠키) 메소드 만 사용하십시오.
컨테이너에 쿠키를 추가 할 때마다 또는 .getCookie를 사용하기 전에 또는 시스템이 컨테이너를 사용하기 전에 Bugfix_Cookiedomain으로 전화하십시오.
private void BugFix_CookieDomain(CookieContainer cookieContainer) { System.Type _ContainerType = typeof(CookieContainer); Hashtable table = (Hashtable)_ContainerType.InvokeMember("m_domainTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, cookieContainer, new object[] { }); ArrayList keys = new ArrayList(table.Keys); foreach (string keyObj in keys) { string key = (keyObj as string); if (key[0] == '.') { string newKey = key.Remove(0, 1); table[newKey] = table[keyObj]; } } }
다른 팁
//bug fix, exists only in 3.5 FW, please wrap it with defines
//http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html
if(!value.Contains("://www.")) //we are going to hit the bug
{
string urlWWW = value.Replace("://", "://www.");
Uri uriWWW = new Uri(urlWWW);
foreach (Cookie c in _cookieContainer.GetCookies(uriWWW))
if (c.Domain.StartsWith("."))
request.Headers["Cookies"] += c.Name + "=" + c.Value + ";"; //manually add the cookies
}
//~bug fix
이 문제로 하루를 잃었습니다. Callmelann의 응답은 도움이되지 않았습니다 (.NET 4.5를 사용하고 있습니다). 제 경우에는 문제가 요청 본문을 설정하고 쿠키를 설정하는 것이 었습니다.
이 경우 쿠키는 서버로 전송되지 않습니다.
var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/");
using (var requestStream = response.GetRequestStream())
{
using (var streamWriter = new StreamWriter(requestStream))
{
requestStream.Write(RequestContent);
}
}
response.CookieContainer.Add(new Cookie("Name", "Value"));
await response.GetResponseAsync();
작업을 변경하려면 순서가 필요합니다.
var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/");
response.CookieContainer.Add(new Cookie("Name", "Value"));
await response.GetResponseAsync();
using (var requestStream = response.GetRequestStream())
{
using (var streamWriter = new StreamWriter(requestStream))
{
requestStream.Write(RequestContent);
}
}
Windows 10 / UWP / .NET Core 앱에서 작동하는이 문제에 대한 수정을 만들었습니다. 문제는 내부를위한 것입니다 CookieContainer
.NET 프레임 워크에 적합한 것처럼 다르지만 엉뚱한 것입니다. 따라서 허용 된 솔루션은 더 이상 작동하지 않습니다.
그러나 "고정"하는 대신 CookieContainer
, 방금 버전을 썼습니다 GetCookies()
"보안"상태에 관계없이 또는 점으로 접두사가있는 경우에 관계없이 특정 도메인에 대한 모든 쿠키를 사용합니다. 귀하의 요구에 적합한대로 자유롭게 수정하십시오. 향후 .NET Core 릴리스에서 구현 된 버전을 얻는 것에 대해 알 수 있습니다.
using System.Collections.Generic;
using System.Reflection;
namespace System.Net
{
/// <summary>
/// Contains extensions for the <see cref="CookieContaner"/> class.
/// </summary>
public static class CookieContainerExtensions
{
/// <summary>
/// Uses Reflection to get ALL of the <see cref="Cookie">Cookies</see> where <see cref="Cookie.Domain"/>
/// contains part of the specified string. Will return cookies for any subdomain, as well as dotted-prefix cookies.
/// </summary>
/// <param name="cookieContainer">The <see cref="CookieContainer"/> to extract the <see cref="Cookie">Cookies</see> from.</param>
/// <param name="domain">The string that contains part of the domain you want to extract cookies for.</param>
/// <returns></returns>
public static IEnumerable<Cookie> GetCookies(this CookieContainer cookieContainer, string domain)
{
var domainTable = GetFieldValue<dynamic>(cookieContainer, "_domainTable");
foreach (var entry in domainTable)
{
string key = GetPropertyValue<string>(entry, "Key");
if (key.Contains(domain))
{
var value = GetPropertyValue<dynamic>(entry, "Value");
var internalList = GetFieldValue<SortedList<string, CookieCollection>>(value, "_list");
foreach (var li in internalList)
{
foreach (Cookie cookie in li.Value)
{
yield return cookie;
}
}
}
}
}
/// <summary>
/// Gets the value of a Field for a given object instance.
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam>
/// <param name="instance">The Type instance to extract the Field's data from.</param>
/// <param name="fieldName">The name of the Field to extract the data from.</param>
/// <returns></returns>
internal static T GetFieldValue<T>(object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
FieldInfo fi = instance.GetType().GetField(fieldName, bindFlags);
return (T)fi.GetValue(instance);
}
/// <summary>
/// Gets the value of a Property for a given object instance.
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam>
/// <param name="instance">The Type instance to extract the Property's data from.</param>
/// <param name="propertyName">The name of the Property to extract the data from.</param>
/// <returns></returns>
internal static T GetPropertyValue<T>(object instance, string propertyName)
{
var pi = instance.GetType().GetProperty(propertyName);
return (T)pi.GetValue(instance, null);
}
}
}
다음은이 버그를 해결하기위한 해킹입니다.http://social.microsoft.com/forums/en-us/netfxnetcom/thread/1297afc1-12d4-4d75-8d3f-756322d234c반사를 사용합니다.