Blawjack
Blawjack

Reputation: 53

How to use asynchronous TNetHTTPClient

How to use asynchronous TNetHTTPClient? I tried following code, but it shows error.

procedure TForm1.Button1Click(Sender: TObject);
var
  httpclient: TNetHTTPClient;
  output: string;
begin
  httpclient := TNetHTTPClient.Create(nil);
  try
      httpclient.Asynchronous := True;
      output := httpclient.Get('https://google.com').ContentAsString;
  finally
    httpclient.Free;
  end;
end;

Error:

Error querying headers: The handle is in a wrong state for the requested operation

Upvotes: 1

Views: 2517

Answers (2)

Peter Bezemek
Peter Bezemek

Reputation: 1

Here you have two versions - one for GET and one for POST (with response). Beware of Modsecurity issues on the server you want to use it on and other server settings which may cause the code not to work as expected (as happened to me when my POST was processed as GET by the server).

For POST, I am using Params.Add('input=' + TNetEncoding.URL.Encode(AText)); you can also use text= instead of input= and I store the response in URLMemo, which you can change to whatever you need.

procedure TUExtractForm.LoadURLAsync(const AURL: string);
const
  CONST_TIMEOUT = 30000;
begin
  TTask.Run(
    procedure
    var
      HttpClient: TNetHTTPClient;
      HTTPRequest: TNetHTTPRequest;
    begin
      try
        HttpClient := TNetHTTPClient.Create(nil);
        try
          HttpClient.ConnectionTimeout := CONST_TIMEOUT; // Timeout
          HttpClient.ResponseTimeout := CONST_TIMEOUT;   // Timeout
          HTTPRequest := TNetHTTPRequest.Create(HttpClient);
          HTTPRequest.Client := HttpClient;
          HTTPRequest.OnRequestCompleted := HTTPRequestCompleted;
          HTTPRequest.OnRequestError := HTTPRequestError;

          HTTPRequest.Get(AURL);
        finally
          HttpClient.Free;
        end;
      except
        on E: Exception do
          begin
            TThread.Queue(nil,
              procedure
              begin
                ShowMessageFmt('Error: %s', [E.Message]);
              end);
          end;
      end;
    end);
end;


procedure TUExtractForm.LoadURLAsyncPOST(const AURL, AText: string);
const
  CONST_TIMEOUT = 30000;
begin
  TTask.Run(
    procedure
    var
      HttpClient: TNetHTTPClient;
      HTTPRequest: TNetHTTPRequest;
      ParamsStream: TBytesStream;
    begin
      ParamsStream := nil;
      try
        HttpClient := TNetHTTPClient.Create(nil);
        try
          HttpClient.ConnectionTimeout := CONST_TIMEOUT; // Timeout
          HttpClient.ResponseTimeout := CONST_TIMEOUT;   // Timeout
          HTTPRequest := TNetHTTPRequest.Create(HttpClient);
          HTTPRequest.Client := HttpClient;
          HTTPRequest.OnRequestCompleted := HTTPRequestCompleted;
          HTTPRequest.OnRequestError := HTTPRequestError;
          HTTPRequest.CustomHeaders['Content-Type'] := 'application/x-www-form-urlencoded';

          ParamsStream := TBytesStream.Create(TEncoding.UTF8.GetBytes('input=' + TNetEncoding.URL.Encode(AText)));
          HTTPRequest.Post(AURL, ParamsStream);
        finally
          HttpClient.Free;
          ParamsStream.Free;
        end;
      except
        on E: Exception do
          begin
            TThread.Queue(nil,
              procedure
              begin
                ShowMessageFmt('Error: %s', [E.Message]);
              end);
          end;
      end;
    end);
end;

procedure TUExtractForm.HTTPRequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
var
  Content: string;
begin
  if AResponse.StatusCode = 200 then
    begin
      Content := AResponse.ContentAsString;
      // replace #$A with new line
      Content := StringReplace(Content, #$A, sLineBreak, [rfReplaceAll]);
      URLMemo.Text := Content;
    end;
end;

procedure TUExtractForm.HTTPRequestError(const Sender: TObject; const AError: string);
begin
  ShowMessageFmt('Error: %s', [AError]);
end;

Upvotes: 0

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28519

In asynchronous mode, as the name implies, client runs request asynchronously in the background thread.

When following code is executed, ContentAsString fails because request is not completed at that point.

output := httpclient.Get('https://google.com').ContentAsString

If you want to use HTTP client in asynchronous mode, you will have to use completion handlers to run appropriate code after request is completed.

procedure TForm1.Button1Click(Sender: TObject);
var
  httpclient: TNetHTTPClient;
begin
  httpclient := TNetHTTPClient.Create(nil);
  httpclient.Asynchronous := True;
  httpclient.OnRequestCompleted := HTTPRequestCompleted;
  httpclient.Get('https://google.com');
end;

procedure TForm1.HTTPRequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
var
  output: string;
begin
  output := AResponse.ContentAsString;
  Sender.Free;
end;

Using HTTP client in asynchronous mode is generally more complicated (especially from the memory management aspect) than using it in synchronous mode from the background thread.

Following is equivalent example using anonymous background thread:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TThread.CreateAnonymousThread(
    procedure
    var
      httpclient: TNetHTTPClient;
      output: string;
    begin
      httpclient := TNetHTTPClient.Create(nil);
      try
        httpclient.Asynchronous := False;
        output := httpclient.Get('https://google.com').ContentAsString;
      finally
        httpclient.Free;
      end;
    end).Start;
end;

Of course, you can also use TTask or custom threads instead of anonymous threads.

Upvotes: 5

Related Questions