在 .NET 开发中请不要序列化 CookieCollection 和 Cookie 对象

笔者之前的文章:摒弃反射:.NET 6 为序列化 CookieContainer 提供了新方法 中所使用的代码对 CookieCollection 对象进行了 JSON 序列化。在该文章发布后笔者对该项目所用的技术投入了实战,然而并不成功。

假设有以下代码:

var cookie = new CookieContainer();
    cookie.Add(new Cookie("c1", "v1", "/", "httpbin.org"));
    cookie.Add(new Cookie("c2", "v2", "/", "httpbin.org"));
    cookie.Add(new Cookie("c3", "v3", "/", "httpbin.org"));
    cookie.Add(new Cookie("c4", "v4", "/", "httpbin.org"));
    var http = new HttpClient(new HttpClientHandler
    {
        UseCookies = true,
        CookieContainer = cookie
    });
    var ret = await http.GetStringAsync("https://httpbin.org/cookies");
    Console.WriteLine(ret);

这会产生正常的输出:

{
      "cookies": {
        "c1": "v1",
        "c2": "v2",
        "c3": "v3",
        "c4": "v4"
      }
}

下面的代码会将一个 CookieCollection 序列化为 JSON 字符串:

static string GetJson()
{
    var cookie = new CookieContainer();
    cookie.Add(new Cookie("c1", "v1", "/", "httpbin.org"));
    cookie.Add(new Cookie("c2", "v2", "/", "httpbin.org"));
    cookie.Add(new Cookie("c3", "v3", "/", "httpbin.org"));
    cookie.Add(new Cookie("c4", "v4", "/", "httpbin.org"));
    var collection = cookie.GetAllCookies();
    var json = JsonConvert.SerializeObject(collection);
    return json;
}

使用反序列化并构造请求:

    var cookie = new CookieContainer();
    var collection = JsonConvert.DeserializeObject<CookieCollection>(GetJson());
    collection.Add(new Cookie("c5", "v5", "/", "httpbin.org"));
    cookie.Add(collection);
    var http = new HttpClient(new HttpClientHandler
    {
        UseCookies = true,
        CookieContainer = cookie
    });
    var ret = await http.GetStringAsync("https://httpbin.org/cookies");
    Console.WriteLine(ret);

发现只有后面手动添加的 c5 被正确输出了:

{
      "cookies": {
        "c5": "v5"
      }
}

该问题的发生原因笔者并未深究,从 LINQPad 的 Dump 输出上看应该不是公共属性差异造成的:

![](https://cdn.coderbusy.com/wp-content/uploads/2021/11/fcff2a91b9f5d5b.png)

一个简单的解决方案是引入一个自定义的 CookieInfo 对象:

    public class CookieInfo
    {
        public CookieInfo()
        {

        }
        public CookieInfo(Cookie cookie)
        {
            this.Name = cookie.Name;
            this.Value = cookie.Value;
            this.Path = cookie.Path;
            this.Domain = cookie.Domain;
        }
        public string Name { get; set; }
        public string Value { get; set; }
        public string Path { get; set; }
        public string Domain { get; set; }
        public Cookie ToCookie()
        {
            return new Cookie(this.Name, this.Value, this.Path, this.Domain);
        }
    }

在序列化和反序列化时,使用 CookieInfo 替代即可:

    static string GetJson()
    {
        var cookie = new CookieContainer();
        cookie.Add(new Cookie("c1", "v1", "/", "httpbin.org"));
        cookie.Add(new Cookie("c2", "v2", "/", "httpbin.org"));
        cookie.Add(new Cookie("c3", "v3", "/", "httpbin.org"));
        cookie.Add(new Cookie("c4", "v4", "/", "httpbin.org"));
        var collection = cookie.GetAllCookies();
        var json = JsonConvert.SerializeObject(collection.Select(i => new CookieInfo(i)));
        return json;
    }

    async Task Main()
    {
        var cookie = new CookieContainer();
        var list = JsonConvert.DeserializeObject<List<CookieInfo>>(GetJson());
        var collection = new CookieCollection();
        foreach (var item in list)
        {
            collection.Add(item.ToCookie());
        }
        collection.Add(new Cookie("c5", "v5", "/", "httpbin.org"));
        cookie.Add(collection);
        var http = new HttpClient(new HttpClientHandler
        {
            UseCookies = true,
            CookieContainer = cookie
        });
        var str = await http.GetStringAsync("https://httpbin.org/cookies");
        Console.WriteLine(str);
    }

原文链接:在 .NET 开发中请不要序列化 CookieCollection 和 Cookie 对象

posted @ 2021-11-19 22:30  Soar、毅  阅读(194)  评论(0编辑  收藏  举报