Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 如何检索XHR响应?_Delphi_Delphi 2007_Chromium Embedded - Fatal编程技术网

Delphi 如何检索XHR响应?

Delphi 如何检索XHR响应?,delphi,delphi-2007,chromium-embedded,Delphi,Delphi 2007,Chromium Embedded,基本上,我用Chromium来显示一个我无法控制的网站。当用户单击特定按钮时,会发送XHR请求;我希望能够找到答案。因此,假设请求发送A,服务器用B进行回复;我想能够读B。我怎么能读呢 似乎有答案,但它只对DCEF有效 我的印象是,制作一个TScheme的后代可以完成这项工作,但我不明白它是如何工作的 在我的情况下,我无法强制DCEF发送请求。我确实需要在用户单击特定按钮时检索该值 缺少OnResourceResponse事件 在一个好的旧CEF1中,您可以简单地使用OnResourceRes

基本上,我用Chromium来显示一个我无法控制的网站。当用户单击特定按钮时,会发送XHR请求;我希望能够找到答案。因此,假设请求发送A,服务器用B进行回复;我想能够读B。我怎么能读呢

  • 似乎有答案,但它只对DCEF有效
  • 我的印象是,制作一个TScheme的后代可以完成这项工作,但我不明白它是如何工作的
  • 在我的情况下,我无法强制DCEF发送请求。我确实需要在用户单击特定按钮时检索该值
缺少OnResourceResponse事件 在一个好的旧CEF1中,您可以简单地使用
OnResourceResponse
事件。在CEF3中,这样一个看似琐碎的任务可能会成为一个真正的挑战,因为与您在这里提出的问题基本相同的链接仍然是开放的,而且似乎唯一的方法(此时)是实现您自己的处理程序,在浏览器和外部世界之间创建代理。实现原理如下图所示:

您可以通过 CefRequestHandler::GetResourceHandler并执行请求/返回 使用CefURLRequest,响应内容为您自己

下面是在DCEF3中要做的事情(此时):

1.定义自己的资源处理程序 首先派生您自己的
TCefResourceHandlerOwn
子代,其中至少实现以下方法:

  • -在这里,您将向服务器转发(发送)请求A,并接收服务器的响应B(这是您第一次使用响应数据B),您应该将其存储(理想情况下存储在类字段中,以便可以轻松地刷新到
    ReadResponse
    方法的输出缓冲区)

  • -在此方法中,您需要填写有关响应B数据长度和(某些)标题字段的输出参数(这可能是您需要对响应B进行标题解析以填充类型参数成员的位置)

  • -在这里,您将刷新响应B数据,以便通过浏览器进行最终处理

2.将资源处理程序分配给请求 下一步是为请求分配您自己的资源处理程序。这在技术上与从Chromium的事件处理程序返回对资源处理程序接口的引用一样简单。但是在这个步骤中,你应该考虑的是,你越是缩小标准,就越简单地生活在资源处理程序中。因为如果您将处理程序分配给任何请求(对于任何URL),那么您可能必须处理并过滤来自服务器的响应,这些响应与您的总体任务完全无关

因此,我建议将分配范围缩小到web应用程序按钮生成的请求,或者至少缩小到请求资源类型(在本例中为
RT\u XHR
),这样您就不需要自己处理所有请求

代码示例 有许多方法可以实现上述步骤。在本例中,我插入了Chromium browser类,并在其中添加了一个新事件
OnXmlHttpExchange
,该事件在XHR请求完成时触发(但在它传递到浏览器之前,它甚至允许您修改响应)


一个简单的XHR小提琴可以在这里找到。
uses
  CefLib, CefVCL;

type
  TXmlHttpExchangeEvent = procedure(Sender: TObject; const Request: ICefRequest; const Response: ICefResponse; DataStream: TMemoryStream) of object;

  TChromium = class(CefVCL.TChromium)
  private
    FOnXmlHttpExchange: TXmlHttpExchangeEvent;
  protected
    procedure DoXmlHttpExchange(const Request: ICefRequest; const Response: ICefResponse; DataStream: TMemoryStream); virtual;
    function doOnGetResourceHandler(const Browser: ICefBrowser; const Frame: ICefFrame; const Request: ICefRequest): ICefResourceHandler; override;
  public
    property OnXmlHttpExchange: TXmlHttpExchangeEvent read FOnXmlHttpExchange write FOnXmlHttpExchange;
  end;

  TXmlHttpHandler = class(TCefResourceHandlerOwn)
  private
    FOwner: TChromium;
    FOffset: NativeUInt;
    FStream: TMemoryStream;
    FCallback: ICefCallback;
    FResponse: ICefResponse;
  protected
    function ProcessRequest(const Request: ICefRequest; const Callback: ICefCallback): Boolean; override;
    procedure GetResponseHeaders(const Response: ICefResponse; out ResponseLength: Int64; out RedirectUrl: ustring); override;
    function ReadResponse(const DataOut: Pointer; BytesToRead: Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean; override;
  public
    constructor Create(Owner: TChromium; const Browser: ICefBrowser; const Frame: ICefFrame; const SchemeName: ustring; const Request: ICefRequest); reintroduce;
    destructor Destroy; override;
    procedure WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt); virtual;
    procedure CompleteRequest(const Request: ICefUrlRequest); virtual;
  end;

  TXmlHttpRequestClient = class(TCefUrlrequestClientOwn)
  private
    FHandler: TXmlHttpHandler;
  protected
    procedure OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt); override;
    procedure OnRequestComplete(const Request: ICefUrlRequest); override;
  public
    constructor Create(Handler: TXmlHttpHandler); reintroduce;
  end;

implementation

{ TChromium }

procedure TChromium.DoXmlHttpExchange(const Request: ICefRequest; const Response: ICefResponse; DataStream: TMemoryStream);
begin
  // fire the OnXmlHttpExchange event
  if Assigned(FOnXmlHttpExchange) then
    FOnXmlHttpExchange(Self, Request, Response, DataStream);
end;

function TChromium.doOnGetResourceHandler(const Browser: ICefBrowser; const Frame: ICefFrame; const Request: ICefRequest): ICefResourceHandler;
begin
  // first trigger the browser's OnGetResourceHandler event
  Result := inherited;
  // if no handler was assigned and request is of type XHR, create our custom one
  if not Assigned(Result) and (Request.ResourceType = RT_XHR) then
    Result := TXmlHttpHandler.Create(Self, Browser, Frame, 'XhrIntercept', Request);
end;

{ TXmlHttpHandler }

constructor TXmlHttpHandler.Create(Owner: TChromium; const Browser: ICefBrowser; const Frame: ICefFrame; const SchemeName: ustring; const Request: ICefRequest);
begin
  inherited Create(Browser, Frame, SchemeName, Request);
  FOwner := Owner;
  FStream := TMemoryStream.Create;
end;

destructor TXmlHttpHandler.Destroy;
begin
  FStream.Free;
  inherited;
end;

function TXmlHttpHandler.ProcessRequest(const Request: ICefRequest; const Callback: ICefCallback): Boolean;
begin
  Result := True;
  // reset the offset value
  FOffset := 0;
  // store the callback reference
  FCallback := Callback;
  // create the URL request that will perform actual data exchange (you can replace
  // it with any other; e.g. with MSXML, or an Indy client)
  TCefUrlRequestRef.New(Request, TXmlHttpRequestClient.Create(Self));
end;

procedure TXmlHttpHandler.GetResponseHeaders(const Response: ICefResponse; out ResponseLength: Int64; out RedirectUrl: ustring);
var
  HeaderMap: ICefStringMultimap;
begin
  // return the size of the data we have in the response stream
  ResponseLength := FStream.Size;
  // fill the header fields from the response returned by the URL request
  Response.Status := FResponse.Status;
  Response.StatusText := FResponse.StatusText;
  Response.MimeType := FResponse.MimeType;
  // copy the header map from the response returned by the URL request
  HeaderMap := TCefStringMultimapOwn.Create;
  FResponse.GetHeaderMap(HeaderMap);
  if HeaderMap.Size <> 0 then
    FResponse.SetHeaderMap(HeaderMap);
end;

function TXmlHttpHandler.ReadResponse(const DataOut: Pointer; BytesToRead: Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean;
begin
  // since this method can be called multiple times (reading in chunks), check if we
  // have still something to transfer
  if FOffset < FStream.Size then
  begin
    Result := True;
    BytesRead := BytesToRead;
    // copy the data from the response stream to the browser buffer
    Move(Pointer(NativeUInt(FStream.Memory) + FOffset)^, DataOut^, BytesRead);
    // increment the offset by the amount of data we just copied
    Inc(FOffset, BytesRead);
  end
  else
    Result := False;
end;

procedure TXmlHttpHandler.WriteResponse(const Request: ICefUrlRequest; Data: Pointer; Size: NativeUInt);
begin
  // write the just downloaded data to the intermediate response stream
  FStream.Write(Data^, Size);
end;

procedure TXmlHttpHandler.CompleteRequest(const Request: ICefUrlRequest);
begin
  FStream.Position := 0;
  // store the response reference for the GetResponseHeaders method
  FResponse := Request.GetResponse;
  // this method is executed when the URL request completes, so we have everything we
  // need to trigger the OnXmlHttpExchange event
  FOwner.DoXmlHttpExchange(Request.GetRequest, FResponse, FStream);
  // this signals the handler that the request has completed and that it can process
  // the response headers and pass the content to the browser
  if Assigned(FCallback) then
    FCallback.Cont;
end;

{ TXmlHttpRequestClient }

constructor TXmlHttpRequestClient.Create(Handler: TXmlHttpHandler);
begin
  inherited Create;
  FHandler := Handler;
end;

procedure TXmlHttpRequestClient.OnDownloadData(const Request: ICefUrlRequest; Data: Pointer; DataLength: NativeUInt);
begin
  FHandler.WriteResponse(Request, Data, DataLength);
end;

procedure TXmlHttpRequestClient.OnRequestComplete(const Request: ICefUrlRequest);
begin
  FHandler.CompleteRequest(Request);
end;
type
  TForm1 = class(TForm)
    Chromium: TChromium;
    procedure FormCreate(Sender: TObject);
  private
    procedure XmlHttpExchange(Sender: TObject; const Request: ICefRequest; const Response: ICefResponse; DataStream: TMemoryStream);
  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chromium.OnXmlHttpExchange := XmlHttpExchange;
end;

procedure TForm1.XmlHttpExchange(Sender: TObject; const Request: ICefRequest; const Response: ICefResponse; DataStream: TMemoryStream);
begin
  // here you can find the request for which you want to read the response (explore the
  // Request parameter members to see by what you can do this)
  if Request.Url = 'http://example.com' then
  begin
    // the DataStream stream contains the response, so process it here as you wish; you
    // can even modify it here if you want
    DataStream.SaveToFile(...);
  end;
end;