Indy 10 - 手动添加 cookie

Indy 10 - add cookies manually

我可以使用“EditThisCookie”为 Google Chrome.

导出我的 cookie

导出如下所示:

"domain": ".whosebug.com",
"expirationDate": 1698322697,
"hostOnly": false,
"httpOnly": false,
"name": "_ga",
"path": "/",
"sameSite": "unspecified",
"secure": false,
"session": false,
"storeId": "0",
"value": "some",
"id": 1    

现在,我想store/parse这个字符串到现有的TIdHTTP Cookie 管理器。

如何使用 TIdCookie 将这些值添加到 Cookie 管理器?

这是我尝试过的方法,但没有成功:

procedure ImportCookies (JSONCookies: string; CookieManager: TIdCookieManager);
var
  StringList: TStringList;
  I : Integer;
  InCookie : Boolean;
  MyCookie: TidCookie;
    
  function BoolStrToBool(s: string): Boolean;
  begin
    if Copy(UpperCase(Trim(s)), 1, 4) = 'TRUE' then Exit(True) else Exit(False);
  end;
    
begin
  InCookie         := False;
  StringList       := TStringList.Create;
  try
    StringList.Text:= JSONCookies;
    for I := 0 to StringList.Count - 1 do
    begin
      if Trim(StringList.Strings[i]) = '{' then
      begin
        InCookie := True;
        MyCookie := CookieManager.CookieCollection.Add;
        Continue;
      end;
    
      if (Trim(StringList.Strings[i]) = '}') or (Trim(StringList.Strings[i]) = '},') then
      begin
        InCookie := False;
        Continue;
      end;
    
      if InCookie then
      begin
        if Copy(StringList.Strings[i], 1, 9) = '"domain":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 10, MaxInt);
          MyCookie.Domain := GetBetween(StringList.Strings[i], '"', '"');
          Continue;
        end;
        if Copy(StringList.Strings[i], 1, 11) = '"hostOnly":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 12, MaxInt);
          MyCookie.HostOnly := BoolStrToBool(StringList.Strings[i]);
          Continue;
        end;
        if Copy(StringList.Strings[i], 1, 11) = '"httpOnly":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 12, MaxInt);
          MyCookie.HttpOnly := BoolStrToBool(StringList.Strings[i]);
          Continue;
        end;
        if Copy (StringList.Strings[i], 1, 7) = '"name":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 8, MaxInt);
          MyCookie.CookieName := GetBetween(StringList.Strings[i], '"', '"');
          Continue;
        end;
        if Copy(StringList.Strings[i], 1, 7) = '"path":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 8, MaxInt);
          MyCookie.Path := GetBetween(StringList.Strings[i], '"', '"');
          Continue;
        end;
        if Copy(StringList.Strings[i], 1, 9) = '"secure":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 10, MaxInt);
          MyCookie.Secure := BoolStrToBool(StringList.Strings[i]);
          Continue;
        end;
        if Copy(StringList.Strings[i], 1, 8) = '"value":' then
        begin
          StringList.Strings[i] := Copy(StringList.Strings[i], 9, MaxInt);
          MyCookie.Path := GetBetween(StringList.Strings[i], '"', '"');
          Continue;
        end;
      end;
    end;
  finally
    StringList.Free;
  end;
end;           

我不明白为什么我需要一个 TIdURI 实例 - 来源应该无关紧要,因为已经有一个目标域了?!

仅供参考,如果您使用实际的 JSON 解析器以及现有的 RTL/library 函数,您的代码可以大大简化,例如:

  • StrUtils.StartsText()IdGlobal.TextStartsWith()
  • SysUtils.StrToBool()
  • SysUtils.AnsiExtractQuotedStr()IdGlobalProtocols.UnquotedStr()
  • IdGlobal.PosInStrAray()
  • 等等

或者,您可以简单地将导出的 cookie 数据重新格式化为标准的 HTTP cookie 格式,然后让 TIdCookieManager.AddServerCookie() 为您完成所有解析(但是您需要 TIdURI)。

无论如何,TIdURITIdCookieManager 的解析器使用,因为 cookie 的某些方面需要根据其来源进行验证。例如,在确定 cookie 的路径时,或将其与 domain/host 匹配时,甚至只是知道 HostOnly cookie 的原始主机是什么,或者是否通过接收到 HttpOnly cookie HTTP 等。cookie 处理、接收 cookie 时以及向服务器发送 cookie 时都有规则,这些规则与 cookie 的来源相关URL。

在您的情况下,由于您正在跳过 TIdCookieManager.AddServerCookie(),您实际上并不需要 TIdURI,因为 Chrome 已经为您完成了繁重的处理。

但是,AddServerCookie() 确实做了一件你没有做的事情,那就是它将 TIdCookie 添加到内部 TIdCookies 列表中,稍后在决定使用哪些 cookie 时会使用该列表在 TIdHTTP 个请求中发送回哪些服务器(参见 TIdCookieManager.GenerateClientCookies())。

因此,仅调用 TIdCookieManager.CookieCollection.Add() 是不够的,它只会创建 TIdCookie 对象。您还需要调用 TIdCookieManager.CookieCollection.AddCookie() - 它也需要一个 TIdURI,尽管这个是可选的,而 AddServerCookie() 中的那个是必需的。

所以,试试这样的东西:

procedure ImportCookies(JSONCookies: string; CookieManager: TIdCookieManager);
var
  StringList: TStringList;
  I : Integer;
  MyCookie: TidCookie;
  S, FieldName, FieldValue: string;

  procedure AddMyCookieIfReady;
  begin
    if MyCookie <> nil then
    begin
      if not CookieManager.CookieCollection.AddCookie(MyCookie, nil) then
      begin
        MyCookie.Collection := nil;
        MyCookie.Free;
      end;
      MyCookie = nil;
    end;
  end;

begin
  StringList := TStringList.Create;
  try
    StringList.Text := JSONCookies;
    MyCookie := nil;

    for I := 0 to StringList.Count - 1 do
    begin
      s := Trim(StringList.Strings[I]);

      if s = '{' then
      begin
        AddMyCookieIfReady;
        MyCookie := CookieManager.CookieCollection.Add;
        Continue;
      end;
    
      if TextStartsWith(s, '}') then
      begin
        AddMyCookieIfReady;
        Continue;
      end;
    
      if (MyCookie = nil) or (not TextStartsWith(s, '"')) then
        Continue;

      Delete(s, 1, 1);
      FieldName := Fetch(s, '"');
      Fetch(s, ':');
      FieldValue := UnquotedStr(TrimLeft(s));

      case PosInStrArray(FieldName, ['domain', 'hostOnly', 'httpOnly', 'name', 'path', 'secure', 'value'], False) of
        0: begin
          if TextStartsWith(FieldValue, '.') then Delete(FieldValue, 1, 1);
          MyCookie.Domain := FieldValue;
        end;
        1: MyCookie.HostOnly := StrToBool(FieldValue);
        2: MyCookie.HttpOnly := StrToBool(FieldValue);
        3: MyCookie.CookieName := FieldValue;
        4: MyCookie.Path := FieldValue;
        5: MyCookie.Secure := StrToBool(FieldValue);
        6: MyCookie.Value := FieldValue;
      end;
    end;

    AddMyCookieIfReady;
  finally
    StringList.Free;
  end;
end;