在 .net 4.5 中尝试使用 HttpClient 使 cookie 无法响应

Posted

技术标签:

【中文标题】在 .net 4.5 中尝试使用 HttpClient 使 cookie 无法响应【英文标题】:Struggling trying to get cookie out of response with HttpClient in .net 4.5 【发布时间】:2012-11-10 00:48:17 【问题描述】:

我有以下代码可以成功运行。我不知道如何从响应中获取 cookie。我的目标是我希望能够在请求中设置 cookie 并从响应中获取 cookie。想法?

private async Task<string> Login(string username, string password)

    try
    
        string url = "http://app.agelessemail.com/account/login/";
        Uri address = new Uri(url);
        var postData = new List<KeyValuePair<string, string>>
        
            new KeyValuePair<string, string>("username", username),
            new KeyValuePair<string, string>("password ", password)
        ;

        HttpContent content = new FormUrlEncodedContent(postData);
        var cookieJar = new CookieContainer();
        var handler = new HttpClientHandler
        
            CookieContainer = cookieJar,
            UseCookies = true,
            UseDefaultCredentials = false
        ;

        var client = new HttpClient(handler)
        
            BaseAddress = address
        ;


        HttpResponseMessage response = await client.PostAsync(url,content);
        response.EnsureSuccessStatusCode();
        string body = await response.Content.ReadAsStringAsync();
        return body;
    
    catch (Exception e)
    
        return e.ToString();
    

这是完整的答案:

HttpResponseMessage response = await client.PostAsync(url,content);
response.EnsureSuccessStatusCode();

Uri uri = new Uri(UrlBase);
var responseCookies = cookieJar.GetCookies(uri);
foreach (Cookie cookie in responseCookies)

    string cookieName = cookie.Name;
    string cookieValue = cookie.Value;

【问题讨论】:

出于好奇,请问您为什么要在客户端读取cookies?我的理解是cookies是用来向服务器发送信息的,不是用来返回信息的。 我在返回 JSON 的调用中使用返回的 cookie,这样我就不必为每个 JSON 调用进行单独的授权调用。也就是说,我有一个调用日志 /Home/GetData ,它返回 JSON 但只有在授权的情况下。在客户端请求中,我添加了 cookie,以便 /Home/GetData 响应。否则它会说“403”未授权。 将授权标头设置为默认标头几乎同样有效且更加标准。服务器无法代表客户端自动设置 auth 标头。 感谢达雷尔的提示。您是否有任何关于 asp.net 中的示例?我为我的 monotouch 和现在的 windows 商店应用程序而苦苦挣扎。如果有一个简单的方法,我会很高兴。这很痛苦,尤其是在 Windows 商店应用程序上使用 async 和 await now。 【参考方案1】:

要将 cookie 添加到请求中,请在请求之前使用 CookieContainer.Add(uri, cookie) 填充 cookie 容器。发出请求后,cookie 容器将自动填充来自响应的所有 cookie。然后,您可以调用 GetCookies() 来检索它们。

CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;

HttpClient client = new HttpClient(handler);
HttpResponseMessage response = client.GetAsync("http://google.com").Result;

Uri uri = new Uri("http://google.com");
IEnumerable<Cookie> responseCookies = cookies.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
    Console.WriteLine(cookie.Name + ": " + cookie.Value);

Console.ReadLine();

【讨论】:

注意:在第一次调用中收到初始cookies后,当访问同域的任何页面时,cookies会自动发送,无需额外步骤。 必须加上“using System.linq;” 如果httpclient 是从 httpclient 工厂构建的,是否有可能以相同的方式获取 cookie?即添加到 services.AddHttpClient 的构建器方法 在获取异步时我们已经引用了“google.com”,为什么我们需要再次声明一个引用它的URI? @FireController1847 如果您想将其用作 IEnumerable,则只需进行转换,因为 GetCookies() 方法返回 CookieCollection,而不是 IEnumerable。如果您所做的只是使用上面代码中的循环,则不需要 IMO 强制转换,因为您仍然可以枚举 CookieCollection,因为它继承了 IEnumerable。【参考方案2】:

如果您无权访问 HttpClient 并且无法注入 CookieContainer,则可以选择其他方法。这适用于 .NET Core 2.2:

private string GetCookie(HttpResponseMessage message)

    message.Headers.TryGetValues("Set-Cookie", out var setCookie);
    var setCookieString = setCookie.Single();
    var cookieTokens = setCookieString.Split(';');
    var firstCookie = cookieTokens.FirstOrDefault();
    var keyValueTokens = firstCookie.Split('=');
    var valueString = keyValueTokens[1];
    var cookieValue = HttpUtility.UrlDecode(valueString);
    return cookieValue;

【讨论】:

刚刚试过这个,var setCookieString = setCookie.Single(); 可能应该更改,因为setCookie 很可能有多个值,从而导致抛出异常。我最终添加了一个参数string cookieName,然后使用了setCookie.Single(x =&gt; x.StartsWith(cookieName));。这仍然假定请求的 cookie 存在。【参考方案3】:

您可以使用给定的 URL 轻松获取 cookie 值。

private async Task<string> GetCookieValue(string url, string cookieName)

    var cookieContainer = new CookieContainer();
    var uri = new Uri(url);
    using (var httpClientHandler = new HttpClientHandler
    
        CookieContainer = cookieContainer
    )
    
        using (var httpClient = new HttpClient(httpClientHandler))
        
            await httpClient.GetAsync(uri);
            var cookie = cookieContainer.GetCookies(uri).Cast<Cookie>().FirstOrDefault(x => x.Name == cookieName);
            return cookie?.Value;
        
    

【讨论】:

【参考方案4】:

并非在所有情况下都可以将httpClientHandler 添加到httpClient。例如,当您使用集成测试testServer.CreateClient() 或从IHttpClientFactory 注入httpClient 时。所以,我只是从标题中读取值。

    public static List<Cookie> GetCookies(this HttpResponseMessage message)
    
        message.Headers.TryGetValues("Set-Cookie", out var cookiesHeader);
        var cookies = cookiesHeader.Select(cookieString => CreateCookie(cookieString)).ToList();
        return cookies;
    

    private static Cookie CreateCookie(string cookieString)
    
        var properties = cookieString.Split(';', StringSplitOptions.TrimEntries);
        var name = properties[0].Split("=")[0];
        var value = properties[0].Split("=")[1];
        var path = properties[2].Replace("path=", "");
        var cookie = new Cookie(name, value, path)
        
            Secure = properties.Contains("secure"),
            HttpOnly = properties.Contains("httponly"),
            Expires = DateTime.Parse(properties[1].Replace("expires=", ""))
        ;
        return cookie;
    

CreateCookie 方法可以修改为与您的 cookie 属性完全匹配。

【讨论】:

以上是关于在 .net 4.5 中尝试使用 HttpClient 使 cookie 无法响应的主要内容,如果未能解决你的问题,请参考以下文章

在 .Net 4.5 中使用 Websocket 的示例 [关闭]

在 Unity 5 中使用 .NET 4.5 代码

.net 4.5 中可绑定的 WPF Richtextbox

并行运行两个异步任务并在 .NET 4.5 中收集结果

MSB3644 - 缺少 .NET 4.5 SDK [关闭]

使用带有 .NET 4.5 HttpClient 的代理