idhttp - 加载页面时获取响应字符串

idhttp - get response string while loading page

我需要在加载页面之前获取接收到的字符串(与 asterix http AMI 事件一起使用)。 所以我试图在 idHttp 的 OnWork 事件中访问接收到的字符串,但出现错误:

var
  Form2: TForm2;
  s:TStringStream;

procedure TForm2.Button1Click(Sender: TObject);
begin
  s:=TStringStream.Create;
  idhttp1.Get('http://website.com:8088/asterisk/rawman?action=waitevent&timeout=10',s);
  showmessage(s.DataString); //NO ERROR
end;

procedure TForm2.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
begin
      showmessage(s.DataString); //ERROR HERE
end;

更新: 我按照 Remy Lebeau 的建议创建了自定义 class (TAMIStringStream),但仍然出现错误。我做错了什么?

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP, cxGraphics, cxControls, cxLookAndFeels,
  cxLookAndFeelPainters, cxContainer, cxEdit, Vcl.StdCtrls, cxTextEdit, cxMemo,
  cxCheckBox;

type
  TAMIStringStream = class(TStringStream)
    FEncoding: TEncoding;
    public
    ReceivedSTR:string;

    function Write(const Buffer; Count: Longint): Longint; override;
  end;

  TForm2 = class(TForm)
    IdHTTP1: TIdHTTP;
    Button1: TButton;
    cxCheckBox1: TcxCheckBox;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  s:TAMIStringStream;

implementation

{$R *.dfm}

function TAMIStringStream.Write(const Buffer; Count: Longint): Longint;
var t:string;
begin
  Inherited;

  t := FEncoding.GetString(Bytes, Position - Count, Count);
  form2.memo1.lines.add(t);
  ReceivedSTR := ReceivedSTR + t;
end;


procedure TForm2.Button1Click(Sender: TObject);
begin
  idhttp1.Get('http://website.com:8088/asterisk/rawman?action=login&username=1cami&secret=111');
  s:=TAMIStringStream.Create;
  while cxCheckBox1.Checked do begin
    idhttp1.Get('http://website.com:8088/asterisk/rawman?action=waitevent&timeout=10',s);
  end;
end;

end.

要在 TIdHTTP 下载服务器的 HTTP 响应数据时获取它,您需要编写自己的 TStream 派生 class 来覆盖虚拟 TStream.Write() 方法,然后您可以将 class 的实例传递给 TIdHTTP.Get()AResponseContent 参数。您的 Write() 方法可以在数据流 "written" 时处理数据(准备好以任意块的形式处理该数据,因为它是实时流式传输)。

否则,您将不得不完全跳过 TIdHTTP 并使用 TIdTCPClient,手动实现 HTTP 协议,以便您完全控制读写。

"AMI over HTTP" 协议文档(参见 this and this)显示了如何向 AMI 发送 HTTP 请求以及如何轮询事件(是的,使用 HTTP 时必须轮询事件)。由于在传递事件之前轮询不会return,因此没有太多理由在运行中读取服务器的响应数据。 TIdHTTP.Get() 会阻塞直到收到事件,然后你可以根据需要进行处理。因此,如果没有自定义流,您的第一种方法应该没问题:

procedure TForm2.Button1Click(Sender: TObject);
var
  s: TStringStream;
begin
  idhttp1.Get('http://website.com:8088/asterisk/rawman?action=login&username=1cami&secret=111');
  s := TStringStream.Create;
  try
    while cxCheckBox1.Checked do
    begin
      IdHttp1.Get('http://website.com:8088/asterisk/rawman?action=waitevent&timeout=10', s);
      Memo1.Lines.Add(s.DataString);
      s.Clear;
    end;
  finally
    s.Free;
  end;
end;

或者:

procedure TForm2.Button1Click(Sender: TObject);
var
  s: String;
begin
  idhttp1.Get('http://website.com:8088/asterisk/rawman?action=login&username=1cami&secret=111');
  while cxCheckBox1.Checked do
  begin
    s := IdHttp1.Get('http://website.com:8088/asterisk/rawman?action=waitevent&timeout=10');
    Memo1.Lines.Add(s);
  end;
end;

由于 TIdHTTP 的阻塞性质,我建议将轮询移动到工作线程中:

procedure TMyThread.Execute;
var
  http: TIdHTTP;
  s: String;
begin
  http := TIdHTTP.Create(nil);
  try
    http.Get('http://website.com:8088/asterisk/rawman?action=login&username=1cami&secret=111');
    while not Terminated do
    begin
      s := http.Get('http://website.com:8088/asterisk/rawman?action=waitevent&timeout=10');
      // do something...
    end;
  finally
    http.Free;
  end;
end;

如果 HTTP 轮询不适合您的需要,您应该考虑使用 "AMI over TCP"(参见 this and this),并为此使用 TIdTCPClient。您可以使用计时器或线程来检查传入数据。