.Net HttpClient 使用 Cookie

Cookie 是服务器存储在客户端的小型数据片段,可用于身份验证、会话跟踪等。

.Net HttpClient 支持 Cookie 功能,本教程详细介绍了Cookie 的管理与使用。

初始化

csharp 复制代码
#!import "./Ini.ipynb"

Cookie 是服务器发送到用户浏览器并存储在本地的一小段数据,用来进行会话管理(如登录状态)、个性化设置(如主题偏好)、跟踪用户行为等功能。

如果只在简单的使用Cookie,想保持简洁、灵活。可以手管理。

发送带Cookie的请求

  • 不使用Cookie
csharp 复制代码
{   //不使用Cookie
    using (var client = new HttpClient(){BaseAddress=new Uri(webApiBaseUrl)})
    {
        var response = await client.GetAsync("/api/Cookie/GetRequestCookie"); 

        //确保请求成功
        response.EnsureSuccessStatusCode();

        //读取响应内容
        var content = await response.Content.ReadAsStringAsync();

        //输出 响应内容
        Console.WriteLine(content);
    }
}
  • 手动设置Cookie: 在默认请求头中添加Cookie,适合快捷请求方法(Get,Post,Put,Delete等)
csharp 复制代码
{   //多次请求时,都自动携带默认请求头Cookie

    var client = new HttpClient()
    {
        BaseAddress = new Uri(webApiBaseUrl),
    };

    //全局设置Cookie,所有快捷请求(Send方法的快捷方法:Get、Post、Put等)都会带上这个Cookie
    //快捷方法,不能单独设置Cookie; 只有Send方法才可以单独设置Cookie
    client.DefaultRequestHeaders.Add("Cookie", "Client=PolyglotNotebook,User=andy");

    //请求1
    var response = await client.GetAsync("/api/Cookie/GetRequestCookie"); 

    //确保请求成功
    response.EnsureSuccessStatusCode();

    //读取响应内容
    var content = await response.Content.ReadAsStringAsync();

    //输出 响应内容
    Console.WriteLine(content);

    //再次快捷请求,不用重新设置
    //请求2
    var response2 = await client.GetAsync("/api/Cookie/GetRequestCookie"); 

    //确保请求成功
    response2.EnsureSuccessStatusCode();

    //读取响应内容
    var content2 = await response2.Content.ReadAsStringAsync();

    //输出 响应内容
    Console.WriteLine(content2);

}
  • 手动设置Cookie:每次请求设置 HttpRequestMessage,适合Send通用方法。当然可以合并默认请求头
csharp 复制代码
{   //手动设置Cookie, 单次HttpRequestMessage合并默认请求头Cookie

    var client = new HttpClient()
    {
        BaseAddress = new Uri(webApiBaseUrl),
    };

    //全局设置Cookie,所有请求都会带上这个Cookie
    client.DefaultRequestHeaders.Add("Cookie", "Client=PolyglotNotebook,User=andy");

    //单请求设置
    var requestMessage = new HttpRequestMessage(HttpMethod.Get, "/api/Cookie/GetRequestCookie");
    
    //设置Cookie,会覆盖HttpClient设置的默认Cookie
    requestMessage.Headers.Add("Cookie", "Password=MyPassword,Role=Admin");
    //添加默认
    if(client.DefaultRequestHeaders.Contains("Cookie"))
    {
        requestMessage.Headers.Add("Cookie", client.DefaultRequestHeaders.GetValues("Cookie"));
    }

    var response = await client.SendAsync(requestMessage);
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(content);

    //响应头也会自动带上Cookie
    if(response.Headers.Contains("cookie"))
    {
        Console.Write($"响应头中Cookie为:");
        var cookies = response.Headers.GetValues("cookie");
        foreach (var cookie in cookies)
        {
            Console.WriteLine($"{cookie}");
        }
    }
    else
    {
        Console.WriteLine($"响应头中没有Cookie");
    }
}

查看响应头中的Cookie

csharp 复制代码
{   //获取响应头中的Cookie

    var client = new HttpClient()
    {
        BaseAddress = new Uri(webApiBaseUrl),
    };

    var response = await client.GetAsync("/api/Cookie/GetResponseCookie");

    //获取响应头中的Cookie
    if(response.Headers.Contains("cookie"))
    {
        Console.Write($"响应头中Cookie为:");
        var cookies = response.Headers.GetValues("cookie");
        foreach (var cookie in cookies)
        {
            Console.WriteLine($"{cookie}");
        }
    }
    else
    {
        Console.WriteLine($"响应头中没有Cookie");
    }
}

.NET 提供了 HttpClientHandler + CookieContainer 来自动管理 Cookie 生命周期和持久化。

使用 CookieContainer

csharp 复制代码
{
    var handler = new HttpClientHandler()
    {
        UseCookies = true,
        CookieContainer = new CookieContainer(),
    };

    using var client = new HttpClient(handler)
    {
        BaseAddress = new Uri(webApiBaseUrl),
    };

    // 第一次请求,服务端设置 Cookie
    var response = await client.GetAsync("/api/Cookie/GetResponseCookie");
    response.EnsureSuccessStatusCode();

    // 第二次请求,自动携带之前设置的 Cookie
    var response2 = await client.GetAsync("/api/Cookie/GetRequestCookie");
    var content2 = await response2.Content.ReadAsStringAsync();
    Console.WriteLine(content2);
}

若需要在程序重启后继续使用 Cookie,可将其序列化保存至文件或数据库。

  • 保存 Cookie 到文件
csharp 复制代码
///<summary
/// 保存Cookie到文件
///</summary>
public void SaveCookies(CookieContainer container, string filePath)
{
    using (var writer = new StreamWriter(filePath))
    {
        foreach (Cookie cookie in container.GetCookies(new Uri(webApiBaseUrl)))
        {
            writer.WriteLine($"{cookie.Name}={cookie.Value};Domain={cookie.Domain};Path={cookie.Path};Expires={cookie.Expires}");
        }
    }
}

//应用
{
    var handler = new HttpClientHandler()
    {
        UseCookies = true,
        CookieContainer = new CookieContainer(),
    };

    using (var client = new HttpClient(handler))
    {
        client.BaseAddress = new Uri(webApiBaseUrl);

        // 第一次请求,服务端设置 Cookie
        var response = await client.GetAsync("/api/Cookie/GetResponseCookie");
        response.EnsureSuccessStatusCode();

        SaveCookies(handler.CookieContainer, "cookies.txt");
    }
}
  • 从文件中加载 Cookie
csharp 复制代码
 <summary>
/// 从文件中加载 Cookie
/// </summary>
public CookieContainer LoadCookies(string filePath)
{
    var container = new CookieContainer();
    if (!File.Exists(filePath)) return container;

    foreach (var line in File.ReadAllLines(filePath))
    {
        var parts = line.Split(';');
        var nameValue = parts[0].Split('=');
        var cookie = new Cookie(nameValue[0], nameValue[1])
        {
            Domain = parts[1].Replace("Domain=", "").Trim(),
            Path = parts[2].Replace("Path=", "").Trim(),
            Expires = DateTime.Parse(parts[3].Replace("Expires=", "").Trim())
        };
        container.Add(cookie);
    }

    return container;
}

//发送请求:从文件中加载 Cookie, 在请求中携带
{
    var cookieBox = LoadCookies("cookies.txt");

    var handler = new HttpClientHandler()
    {
        UseCookies = true,
        CookieContainer = cookieBox,
    };

    var client = new HttpClient(handler)
    {
        BaseAddress = new Uri(webApiBaseUrl)
    };

    // 第一次请求,服务端设置 Cookie
    var response = await client.GetAsync("/api/Cookie/GetRequestCookie");
    response.EnsureSuccessStatusCode();

    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine(content);
}

默认情况下,CookieContainer 会根据域名自动隔离 Cookie。若需跨域共享 Cookie,可通过以下方式实现:

  • 手动复制 Cookie
csharp 复制代码
//示例代码,无实际请求
var sourceUri = new Uri("https://source.com");
var targetUri = new Uri("https://target.com");

foreach (Cookie cookie in handler.CookieContainer.GetCookies(sourceUri))
{
    var crossDomainCookie = new Cookie(cookie.Name, cookie.Value, cookie.Path, targetUri.Host);
    handler.CookieContainer.Add(targetUri, crossDomainCookie);
}
  • 自定义 CookieContainer(高级)
    可以继承 CookieContainer 并重写相关方法以实现自定义 Cookie 共享策略。

进阶功能:Cookie 过期、安全标志、SameSite 设置等

  • 设置 Cookie 高级属性
csharp 复制代码
{
    //发送请求
    {
        var handler = new HttpClientHandler()
        {
            UseCookies = true,
            CookieContainer = new CookieContainer(),
        };

        var client = new HttpClient(handler)
        {
            BaseAddress = new Uri(webApiBaseUrl)
        };

        //设置Cookie
        var cookie = new Cookie("jwt_token", "a.b.c")
        {
            Expires = DateTime.Now.AddDays(7),
            Domain = new Uri(webApiBaseUrl).Host,
            Path = "/",
            Secure = true,           // 仅 HTTPS 传输
            HttpOnly = true,         // 防止 XSS 攻击
        };

        handler.CookieContainer.Add(new Uri(webApiBaseUrl), cookie);

        // 第一次请求,服务端设置 Cookie
        var response = await client.GetAsync("/api/Cookie/GetRequestCookie");
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();

        Console.WriteLine(content);
    }
}

总结

功能 描述 适用场景
手动设置 Cookie 灵活、简单 单次请求或固定 Cookie
使用 HttpRequestMessage 更精细控制 Cookie 行为 需要合并默认与自定义 Cookie
使用 CookieContainer 自动管理 Cookie 生命周期 多次请求、需要保持会话状态
Cookie 持久化 保存 Cookie 至文件或数据库 程序重启后仍需保持登录状态
跨域 Cookie 手动复制或自定义容器 需要在多个域名之间共享 Cookie
安全 Cookie 设置 SecureHttpOnlySameSite 增强 Cookie 安全性
获取 Cookie 属性 查看 Cookie 的有效期、路径等信息 调试和日志记录
相关推荐
WineMonk3 小时前
ArcGIS Pro 3.4 二次开发 - 框架
arcgis·.net·arcgispro
亿牛云爬虫专家5 小时前
Playwright 多语言一体化——Python_Java_.NET 全栈采集实战
java·python·c#·汽车·.net·playwright·dongchedi.com
Kookoos7 小时前
使用 ABP vNext 集成 MinIO 构建高可用 BLOB 存储服务
后端·c#·.net·.netcore·minio·blob
WineMonk8 小时前
ArcGIS Pro 3.4 二次开发 - 内容
arcgis·.net
SEO-狼术9 小时前
Oxygen Feedback by Syncro Soft Crack
.net
界面开发小八哥9 小时前
界面组件DevExpress WPF中文教程:Grid - 如何自定义Band Header外观?
.net·wpf·界面控件·devexpress·ui开发·用户界面
o0向阳而生0o11 小时前
47、C#可否对内存进⾏直接的操作?
开发语言·c#·.net
快乐飒男12 小时前
c#基础01(.Net介绍)
开发语言·c#·.net
码观天工14 小时前
.NET 原生驾驭 AI 新基建实战系列(八):总结篇 ── 数据库技术的革命:从结构化到非结构化再到智能化的演进
ai·c#·.net·向量数据库