Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.
If you find these projects useful please consider becoming a sponsor with Patreon, GitHub or Liberapay.

Get Content from Network request

Post Reply
nanaksr
Posts: 7
Joined: Wed Mar 25, 2020 6:05 am

Get Content from Network request

Post by nanaksr »

Hello,

How to retrieve data from the network request response as shown below.

Image

I've tried looking at the MiniBrowser demo, but it's only fetching the Key & Value headers, I want to fetch its contents.
User avatar
salvadordf
Posts: 4563
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Get Content from Network request

Post by salvadordf »

Hi,

The MiniBrowser demo gets the contents of the HTML file that is used in the "Save resource as..." menu option. You can do the same to get that JSON data.

You need to be able to identify the request with the JSON contents.

You will need to call TWVBrowser.AddWebResourceRequestedFilter with a COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST or COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL parameter. See the WVBrowser1AfterCreated procedure in MiniBrowser.

Implement the TWVBrowser.OnWebResourceResponseReceived event like this :
https://github.com/salvadordf/WebView4Delphi/blob/3217ffe723e053b875e73d4dc34a3b4d0d24a126/demos/Delphi_VCL/MiniBrowser/uMiniBrowser.pas#L878

Declare a "TempRequest" variable as TCoreWebView2WebResourceRequestRef and create it like this to compare the TempRequest.URI if necessary :

Code: Select all

TempRequest := TCoreWebView2WebResourceRequestRef.Create(TempArgs.Request);
Then call TempResponse.GetContent(TempHandler); and implement TWVBrowser.OnWebResourceResponseViewGetContentCompleted to get the contents like this :
https://github.com/salvadordf/WebView4Delphi/blob/3217ffe723e053b875e73d4dc34a3b4d0d24a126/demos/Delphi_VCL/MiniBrowser/uMiniBrowser.pas#L898
nanaksr
Posts: 7
Joined: Wed Mar 25, 2020 6:05 am

Re: Get Content from Network request

Post by nanaksr »

I try with

Code: Select all

procedure TForm1.WVBrowser1AfterCreated(Sender: TObject);
begin
  WVWindowParent1.UpdateSize;
  WVBrowser1.AddWebResourceRequestedFilter('*', COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
//  WVBrowser1.AddWebResourceRequestedFilter('*', COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST);
end;

procedure TForm1.WVBrowser1WebResourceResponseReceived(Sender: TObject;
  const aWebView: ICoreWebView2;
  const aArgs: ICoreWebView2WebResourceResponseReceivedEventArgs);
var
  TempArgs     : TCoreWebView2WebResourceResponseReceivedEventArgs;
  TempResponse : TCoreWebView2WebResourceResponseView;
  TempRequest  : TCoreWebView2WebResourceRequestRef;
  TempHandler  : ICoreWebView2WebResourceResponseViewGetContentCompletedHandler;
begin
  try
    TempArgs     := TCoreWebView2WebResourceResponseReceivedEventArgs.Create(aArgs);
    TempRequest  := TCoreWebView2WebResourceRequestRef.Create(TempArgs.Request);
    TempHandler  := TCoreWebView2WebResourceResponseViewGetContentCompletedHandler.Create(WVBrowser1);

    Memo1.Lines.Add(TempRequest.URI);

    if TempRequest.URI = 'https://mcm2.bankmandiri.co.id/corporate/loginfo/getApmUrl' then
      TempResponse.GetContent(TempHandler);

  finally
    FreeAndNil(TempRequest);
    FreeAndNil(TempArgs);
    TempHandler := nil;
  end;
end;

procedure TForm1.WVBrowser1WebResourceResponseViewGetContentCompleted(
  Sender: TObject; aErrorCode: HRESULT; const aContents: IStream;
  aResourceID: Integer);
var
  TempOLEStream : TOLEStream;
begin
  TempOLEStream := nil;
  try
    if succeeded(aErrorCode) and assigned(aContents) then
      begin

        TempOLEStream          := TOLEStream.Create(aContents);
        TempOLEStream.Position := 0;

        if (TempOLEStream.Size > 0) then
          begin
            SetLength(FResourceContents, TempOLEStream.Size);
            TempOLEStream.Read(FResourceContents, TempOLEStream.Size);
          end;
      end;
  finally
    if assigned(TempOLEStream) then
      FreeAndNil(TempOLEStream);
  end;
end;
Error In

Code: Select all

function TCoreWebView2WebResourceResponseView.GetInitialized : boolean;
begin
  Result := assigned(FBaseIntf);
end;
User avatar
salvadordf
Posts: 4563
Joined: Thu Feb 02, 2017 12:24 pm
Location: Spain
Contact:

Re: Get Content from Network request

Post by salvadordf »

TempResponse has to be created before getting TempResponse.URI. Add this line :

Code: Select all

TempResponse := TCoreWebView2WebResourceResponseView.Create(TempArgs.Response);
nanaksr
Posts: 7
Joined: Wed Mar 25, 2020 6:05 am

Re: Get Content from Network request

Post by nanaksr »

Many Thanks Perfect Working

Image
hamden
Posts: 1
Joined: Sat Jan 27, 2024 11:27 pm

Re: Get Content from Network request

Post by hamden »

Hi;
I modified the SimpleBrowser_D7 source to implement all necessary and required events in order to take that response (taken from token endpoint);
Delphi 7 version below;
First place a memo on the form;
It is working properly and the response will added in the memo line;

Code: Select all

unit uSimpleBrowser;

interface

{$I webview2.inc}

uses
  {$IFDEF DELPHI16_UP}
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.StdCtrls,
  {$ELSE}
  Windows, Messages, SysUtils, Variants, Classes, Graphics,
  Controls, Forms, Dialogs, ExtCtrls, ComCtrls, StdCtrls,
  {$ENDIF}
  uWVBrowser, uWVWinControl, uWVWindowParent, uWVTypes, uWVConstants, uWVTypeLibrary,
  uWVLibFunctions, uWVLoader, uWVInterfaces, uWVCoreWebView2Args,
  uWVBrowserBase, uWVCoreWebView2WebResourceResponseView, uWVCoreWebView2WebResourceRequest,
  uWVCoreWebView2Delegates, ActiveX, AxCtrls;

type
  TMainForm = class(TForm)
    WVWindowParent1: TWVWindowParent;
    Timer1: TTimer;
    WVBrowser1: TWVBrowser;
    AddressPnl: TPanel;
    AddressCb: TComboBox;
    GoBtn: TButton;
    Memo1: TMemo;

    procedure Timer1Timer(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure GoBtnClick(Sender: TObject);

    procedure WVBrowser1AfterCreated(Sender: TObject);
    procedure WVBrowser1DocumentTitleChanged(Sender: TObject);
    procedure WVBrowser1InitializationError(Sender: TObject; aErrorCode: HRESULT; const aErrorMessage: wvstring);
    procedure WVBrowser1WebResourceResponseReceived(Sender: TObject;
      const aWebView: ICoreWebView2;
      const aArgs: ICoreWebView2WebResourceResponseReceivedEventArgs);
    procedure WVBrowser1WebResourceResponseViewGetContentCompleted(
      Sender: TObject; aErrorCode: HRESULT; const aContents: IStream;
      aResourceID: Integer);

  protected
    // It's necessary to handle these messages to call NotifyParentWindowPositionChanged or some page elements will be misaligned.
    procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
    procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
  public
    { Public declarations }
    function MemoryStreamToString(M: TMemoryStream): AnsiString;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

// This is a demo with the simplest web browser you can build using WebView4Delphi and
// it doesn't show any sign of progress like other web browsers do.

// Remember that it may take a few seconds to load if Windows update, your antivirus or
// any other windows service is using your hard drive.

// Depending on your internet connection it may take longer than expected.

// Please check that your firewall or antivirus are not blocking this application
// or the domain "bing.com".

// The "initialization" section in this unit loads GlobalWebView2Loader which will create
// the global environment asynchronously.

// The browser needs the global environment but the form might be created before that so we
// use a simple timer to create the browser in case the environment is not ready when
// TForm.OnShow is triggered.

// GlobalWebView2Loader will be destroyed automatically in the "finalization" section of
// uWVLoader.pas. All browsers should be already destroyed before GlobalWebView2Loader
// is destroyed.

procedure TMainForm.FormShow(Sender: TObject);
begin
  if GlobalWebView2Loader.InitializationError then
    showmessage(GlobalWebView2Loader.ErrorMessage)
   else
    if GlobalWebView2Loader.Initialized then
      WVBrowser1.CreateBrowser(WVWindowParent1.Handle)
     else
      Timer1.Enabled := True;
end;

procedure TMainForm.GoBtnClick(Sender: TObject);
begin
  WVBrowser1.Navigate(AddressCb.Text);
end;

procedure TMainForm.WVBrowser1AfterCreated(Sender: TObject);
begin
  WVWindowParent1.UpdateSize;
  WVWindowParent1.SetFocus;
  Caption := 'SimpleBrowser';
  AddressPnl.Enabled := True;
  WVBrowser1.AddWebResourceRequestedFilter('*', COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
  //WVBrowser1.AddWebResourceRequestedFilter('*', COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST);
end;

procedure TMainForm.WVBrowser1DocumentTitleChanged(Sender: TObject);
begin
  Caption := 'SimpleBrowser - ' + WVBrowser1.DocumentTitle;
end;

procedure TMainForm.WVBrowser1InitializationError(Sender: TObject;
  aErrorCode: HRESULT; const aErrorMessage: wvstring);
begin
  showmessage(aErrorMessage);
end;

procedure TMainForm.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;

  if GlobalWebView2Loader.Initialized then
    WVBrowser1.CreateBrowser(WVWindowParent1.Handle)
   else
    Timer1.Enabled := True;
end;

procedure TMainForm.WMMove(var aMessage : TWMMove);
begin
  inherited;

  if (WVBrowser1 <> nil) then
    WVBrowser1.NotifyParentWindowPositionChanged;
end;

procedure TMainForm.WMMoving(var aMessage : TMessage);
begin
  inherited;

  if (WVBrowser1 <> nil) then
    WVBrowser1.NotifyParentWindowPositionChanged;
end;

procedure TMainForm.WVBrowser1WebResourceResponseReceived(Sender: TObject;
  const aWebView: ICoreWebView2;
  const aArgs: ICoreWebView2WebResourceResponseReceivedEventArgs);
var
  TempArgs: TCoreWebView2WebResourceResponseReceivedEventArgs;
  TempResponse : TCoreWebView2WebResourceResponseView;
  TempRequest  : TCoreWebView2WebResourceRequestRef;
  TempHandler  : ICoreWebView2WebResourceResponseViewGetContentCompletedHandler;
  statusCode: integer;
begin
  try
    TempArgs     := TCoreWebView2WebResourceResponseReceivedEventArgs.Create(aArgs);
    TempRequest  := TCoreWebView2WebResourceRequestRef.Create(TempArgs.Request);
    TempHandler  := TCoreWebView2WebResourceResponseViewGetContentCompletedHandler.Create(WVBrowser1);
    TempResponse := TCoreWebView2WebResourceResponseView.Create(TempArgs.Response);

    Memo1.Lines.Add(TempRequest.URI);

    if TempRequest.URI = 'https://mywebsite/auth/token' then
    begin
      TempResponse.GetContent(TempHandler);
      statusCode := TempResponse.StatusCode;
      Memo1.Lines.Add('statusCode: ' + IntToStr(statusCode) + ' TempRequest.Method ' + TempRequest.Method)
    end;

  finally
    FreeAndNil(TempRequest);
    FreeAndNil(TempArgs);
    TempHandler := nil;
  end;
end;

procedure TMainForm.WVBrowser1WebResourceResponseViewGetContentCompleted(
  Sender: TObject; aErrorCode: HRESULT; const aContents: IStream;
  aResourceID: Integer);
var
  tempOLEStream : TOLEStream;
  temp: Largeint;
  memoryStream: TMemorystream;
  strContents: string;
begin
  TempOLEStream := nil;
  MemoryStream := TMemoryStream.Create;
  try
    if succeeded(aErrorCode) and assigned(aContents) then
      begin
        aContents.Seek(0, STREAM_SEEK_SET, Temp);
        tempOLEStream := TOLEStream.Create(aContents);
        tempOLEStream.Position := 0;

        if (TempOLEStream.Size > 0) then
          begin
            MemoryStream.CopyFrom(tempOLEStream, tempOLEStream.Size);
            strContents := MemoryStreamToString(memoryStream);
            Memo1.Lines.Add(strContents);
          end;
      end;
  finally
    if assigned(TempOLEStream) then
      FreeAndNil(TempOLEStream);
  end;
end;

function TMainForm.MemoryStreamToString(M: TMemoryStream): AnsiString;
begin
  SetString(Result, PAnsiChar(M.Memory), M.Size);
end;

initialization
  GlobalWebView2Loader                := TWVLoader.Create(nil);
  GlobalWebView2Loader.UserDataFolder := ExtractFileDir(Application.ExeName) + '\CustomCache';
  GlobalWebView2Loader.StartWebView2;

end.
Arioch
Posts: 2
Joined: Tue Jan 21, 2025 12:57 pm

Re: Get Content from Network request

Post by Arioch »

hamden wrote: Sat Jan 27, 2024 11:36 pm I modified the SimpleBrowser_D7 source to implement all necessary and required events

Code: Select all

    // It's necessary to handle these messages to call NotifyParentWindowPositionChanged or some page elements will be misaligned.
    procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
    procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
Hello, @hamden

Can you show any specific case about "some page elements will be misaligned." ?

I commented out these two functions and i see nothing bad in Win 10 + Delphi 2007, maybe i don't know where to look...

Microsoft docs are VERY ambiguous there.
  • https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2controller?view=webview2-1.0.2903.40#notifyparentwindowpositionchanged
  • https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2controller.notifyparentwindowpositionchanged?view=webview2-dotnet-1.0.2792.45
This is needed for accessibility and certain dialogs in WebView to work correctly.
Tells WebView that the main WebView parent (or any ancestor) HWND moved.
Really, what should it mean?..

I played a bit with the Simple Demo (after commenting out WM_MOV*) and could not trigger any bug.

-----

Your quick-fix is ok for a demo, but would hardly be a good thing for a library.
  • It is unsafe-by-default. Unless made a part of the control itself - it would be mostly forgotten by the library users.
  • It is very boilerplaty and error-prone...
  • It is not enough
  • Is it even needed really?
About being enough, for this simplistic demo the "or any ancestor HWND" clause would not matter, but in most practical implementation the browser would be placed in some panel and/or pagecontrol and/or frame/CCPack, etc

I modified the Simple Demo

Code: Select all

  TPanel = class(ExtCtrls.TPanel)
  private
    procedure WMMove(var aMessage : TWMMove); message WM_MOVE;
    procedure WMMoving(var aMessage : TMessage); message WM_MOVING;
  end;

  TMainForm = class(TForm)
When i run it there was one single call to TPanel.WMMove and nothing after it.
So, we may think in practical applications form-level events would not be enough.
If, against the intuition, Windows or VCL would be sending those to child windows, all we would have to do would be a simplistic modification in the procedure TWVWindowParent.WndProc(var aMessage: TMessage); but sadly it would not work

There are hooking facilities in Windows, SetWindowsHookEx and SetWinEventHook: https://stackoverflow.com/a/22025978/976391
However those would be dependent upon filtering HWND real quick.
It would not be hard to add the global TObjectList keeping track of all the created browsers.

However should we do naive iterating through all of them?
Because mere caching the HWND parenting chain would be fragile in case of later re-parenting.
Or should we somehow hook the re-parenting events (i think i saw something about it, but i am not sure) and invalidate those chains?

Make me convinced NotifyParentWindowPositionChanged is worth attention at all, or we have to do it properly, which is kinda demanding :-D

----
Last edited by Arioch on Tue Jan 21, 2025 1:39 pm, edited 1 time in total.
Arioch
Posts: 2
Joined: Tue Jan 21, 2025 12:57 pm

Re: Get Content from Network request

Post by Arioch »

@salvadordf while i registered here i still think accumulating information inside GitHub itself is better w.r.t. "bus factor", and yet better in the repository itself.

Things like "concepts", "gotchas" and "implementation hard choices" naturally belong to documentation, like made in the fantastic VTV documentation pages, if you copy.

Though chosing any format to keep documentation in, other than XMLDoc, would probably pin you to a specific toolchain. OTOH I am not sure if XMLDoc (and its compatibility layer in PasDoc) provides for having separate documentation-only files...
Post Reply