All*_*ain 2 delphi delphi-2007 chromium-embedded
基本上,我使用Chromium来显示一个我无法控制的网站.当用户点击特定按钮时,发送XHR请求; 我希望能够找到答案.所以,假设请求发送A,服务器回复B; 我希望能够阅读B.我怎么能这样做?
在一个好的旧CEF1中你可以简单地使用这个OnResourceResponse
事件.在CEF3中,这样一个看似微不足道的任务可能会成为一个真正的挑战,因为Issue 515
链接the answer
到你在这里提出的基本相同的问题仍然是打开的,似乎唯一的方法(此时)正在实现你自己的CefResourceHandler
处理程序浏览器和外面的世界之间的代理.实现原理如下所述similar topic
:
您可以通过CefRequestHandler :: GetResourceHandler使用CefResourceHandler并使用CefURLRequest自行执行请求/返回响应内容.
所以这里是在DCEF3中做什么(此时):
首先派生自己的TCefResourceHandlerOwn
后代,至少实现以下方法:
ProcessRequest
- 在这里,您将向服务器转发(发送)请求A并接收服务器的响应B(这是您第一次使用响应数据B的机会),您应该保存它(理想情况是在类字段中,以便可以刷新ReadResponse
很容易到方法的输出缓冲区).
GetResponseHeaders
- 在这个方法中,你需要填写关于响应B数据长度和(某些)头字段的输出参数(这可能是你需要为头文件解析你的响应B来填充CefResponse
类型参数的地方成员).
ReadResponse
- 这是您将刷新响应B数据以供浏览器进行最终处理的地方.
下一步是为请求分配您自己的资源处理程序.从技术上讲,这与从Chromium的OnGetResourceHandler
事件处理程序返回对资源处理程序接口的引用一样简单.但是在这一步中你应该考虑越是缩小标准,你就越容易在资源处理程序中生存.因为如果您将处理程序分配给任何请求(对于任何URL),那么您可能必须处理并过滤掉与您的整体任务完全无关的服务器中的响应.
因此,我建议将分配范围缩小到该Web应用程序按钮生成的请求,或至少通过请求资源类型(RT_XHR
在本例中),这样您就不需要自己处理所有请求.
如何实现上述步骤有很多方法.在这个例子中,我插入了Chromium浏览器类,并在那里添加了一个新事件OnXmlHttpExchange
,当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;
Run Code Online (Sandbox Code Playgroud)
并且可以使用这个插入的类:
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;
Run Code Online (Sandbox Code Playgroud)