HTTP.post versus downloadURL using Delphi XE2 - delphi

I am trying to use the same procedure for two types of downloads. both are working.
I would like to use TDownloadURL or HTTP.Post on both, but cannot determine how. Which method and how to do this? Thank you.
First operation --
procedure TfrmMain.get1Click(Sender: TObject);
var
json: string;
lHTTP: TIdHTTP;
lParamList: TStringList;
result:string;
begin
json := CRLF +
'{' + CRLF +
' "resource_id": "391792b5-9c0a-48a1-918f-2ee63caa1c54",' + CRLF +
' "filters": {' + CRLF +
' "provider_id": 393303' + CRLF +
' }' + CRLF +
'}';
lParamList := TStringList.Create;
try
lParamList.Add('somename='+json);
lHTTP := TIdHTTP.Create(nil);
try
Result := lHTTP.Post('http://hub.Healthdata.gov/api/action/datastore_search', lParamList);
finally
lHTTP.Free;
end;
finally
lParamList.Free;
end;
end;
Second operation --
procedure TfrmMain.get2Click(Sender: TObject);
var
dl: TDownloadURL;
url:string;
begin
url:='http://api.census.gov/data/2010/sf1?key=KEY&get=P0010001,NAME&for=state:*';
dl := TDownloadURL.Create(self);
try
dl.URL := url;
dl.FileName := execpath+'api1.txt'; dl.ExecuteTarget(nil); dl.Free;
except
dl.Free;
end;
end;

TDownloadURL uses the GET HTTP method. TIdHTTP.Post obviously uses the POST method. In general, neither is appropriate for use in place of the other. That's why both methods exist in the first place.
A POST request can include all the information that a GET request does, plus more, which makes it seem like it should be able to do everything GET can do, plus more. However, servers are not required to (and should not be expected to) handle POST requests the same way they do GET.
As the one writing the HTTP client, you're not really in control of the situation. The server dictates which methods it will honor. Clients need to either do what's expected of them or be denied access.
The Indy components support both methods, so if you just want to make your POST code and your GET code look similar, then you can replace TDownloadURL with TIdHTTP.Get.

Related

Delphi - TIdHTTP freezes when POSTing data

I have an issue developing an HTTP client using Indy's TIdHTTP component. I'm using Indy 10.5.9.0.
When I call the TIdHTTP.DoRequest() method, the component freezes, and raises an exception:
Connection closed gracefully
I already tried using the TIdHTTP.Post() method, but the same problem happens.
The problem doesn't happen using the TIdHTTP.Get() method.
Here is my code:
try
jsonLatestFirmware := TJSONObject.ParseJSONValue(httpClient.Get(strGetLatest)) as TJSONObject;
try
srv := 'http://' + currentAlive.IP + ':9335/upgrade?command=start';
sList := TStringList.Create;
sList.Text := jsonLatestFirmware.get('data').JsonValue.ToString;
fileName := ExtractFilePath(Application.ExeName) + 'finals\' + currentMACQR + '-' + currentAlive.IP + '.json';
sList.SaveToFile(fileName);
JsonRetorno := TStringStream.Create('');
try
JsonToSend := TStringStream.Create;
WriteStringToStream(JsonToSend,TIdHTTPAccess(httpClient).SetRequestParams(sList, TEncoding.UTF8));
JsonToSend.Position := 0;
TIdHTTPAccess(httpClient).DoRequest('POST',srv,JsonToSend,jsonRetorno,[]); //component freezes here...
strRetorno := TStringList.Create();
strRetorno.LoadFromStream(JsonRetorno);
lblInformacao.Visible := True;
ShowMessage(strRetorno.Text);
except
on E: Exception do
ShowMessage('Error on request: '#13#10 + E.Message);
end;
finally
sList.Free;
JsonRetorno.Free;
JsonToSend.Free;
jsonResponse.Free;
end;
except
on E: Exception do
ShowMessage('There are no firmwares available for this product.');
end;
Anyone can help me to solve this problem?
I'm using Indy 10.5.9.0.
That is a very old and outdated version. At the time of this writing, the latest version is 10.6.2.5455. Please upgrade, as there have been a lot of changes and fixes to Indy since 10.5.9.
When I call the TIdHTTP.Post() method, the component freezes, and raises an exception:
First, you are not calling TIdHTTP.Post(), you are calling TIdHTTP.DoRequest(). You should be calling TIdHTTP.Post() instead, which calls TIdHTTP.DoRequest() internally for you:
httpClient.Post(srv, JsonToSend, jsonRetorno);
Second, Post() can't freeze and raise an exception at the same time. Which is actually happening? How large is the JSON you are posting? Does the server send any kind of response before the exception is raised?
Third, you absolutely should NOT be calling TIdHTTP.SetRequestParams() at all. That method is meant for internal use by the TStrings overloaded version of TIdHTTP.Post() when it submits an HTML webform in application/x-www-webform-urlencoded format. That is NOT what you need in this situation. You need to post your JSON as-is, not webform-encode it.
Indy uses blocking sockets. Most operations in Indy are synchronous. TIdHTTP.Post() is no different. It blocks the calling thread until the HTTP post is finished. If it freezes, that means the post is taking a long time to send, or the server is not sending a valid response back. If TIdHTTP.Post() raises a "Connection closed gracefully" exception, that means the server has closed the socket connection on its end while TIdHTTP was still sending/reading data over the socket. That could happen, for instance, if the server doesn't like your POST request, or if it determines the JSON is invalid while it is being received.
Also, your code can be simplified in general. You are misusing TStringList for handling JSON data.
Try something more like this instead:
var
sJSON, sRetorno: string;
jsonLatestFirmware: TJSONValue;
JsonToSend: TStringStream;
...
begin
...
try
sJSON := httpClient.Get(strGetLatest);
except
ShowMessage('There are no firmwares available for this product.');
Exit;
end;
try
jsonLatestFirmware := TJSONObject.ParseJSONValue(sJSON);
try
sJson := (jsonLatestFirmware as TJSONObject).get('data').JsonValue.ToString;
finally
jsonLatestFirmware.Free;
end;
JsonToSend := TStringStream.Create(sJson, TEncoding.UTF8);
try
JsonToSend.SaveToFile(ExtractFilePath(Application.ExeName) + 'finals\' + currentMACQR + '-' + currentAlive.IP + '.json');
httpClient.Request.ContentType := 'application/json';
sRetorno := httpClient.Post('http://' + currentAlive.IP + ':9335/upgrade?command=start', JsonToSend);
finally
JsonToSend.Free;
end;
except
on E: Exception do
ShowMessage('Error: '#13#10 + E.Message);
Exit;
end;
lblInformacao.Visible := True;
ShowMessage(sRetorno);
...
end;

Error on Indy HTTP post using TStringList as sending parameter

I have created a little program which access a webservice of mail service. The same code works on Delphi 7 with indy 9, but doesn't works with Delphi Seattle with Indy 10.
My Stringlist is built that way:
ParametrosConsulta.Values['nCdEmpresa'] := Edt_Cod.Text;
ParametrosConsulta.Values['&sDsSenha'] := Edt_Sen.Text;
...
My post have a sending parameter a stringlist, which has a text like that:
nCdEmpresa= &sDsSenha= &nCdServico=41106&sCepOrigem=88905355&sCepDestino=88906768&nVlPeso=20.0&nCdFormato=1&nVlComprimento=20.0&nVlAltura=20.0&nVlLargura=20.0&nVlDiametro=6.0&sCdMaoPropria=N&nVlValorDeclarado=0&sCdAvisoRecebimento=N
then i call idHttp.Post like that, which ParametroConsulta holds the text i've shown before, and Resposta is a TStringStream which holds the response of the request:
IdHttp.Request.Clear;
IdHttp.Request.Host := 'ws.correios.com.br';
IdHttp.Request.ContentType := 'application/x-www-form-urlencoded';
idHTTP.Request.UserAgent := 'Mozilla/3.0 (compatible;Indy Library)';
IdHTTP.Request.Charset := 'utf-8';
IdHTTP.ProtocolVersion := pv1_1;
{...}
try
Application.ProcessMessages;
FrmPrincipal.Refresh;
IdHttp.Post('http://ws.correios.com.br/calculador/CalcPrecoPrazo.asmx/CalcPrecoPrazo', ParametrosConsulta, Resposta);
except
on E:EIdHttpProtocolException do
begin
ShowMessage(E.Message + ' - ' + E.ErrorMessage);
PnlEnviando.Visible := False;
Exit;
end;
end;
But after post, the webservice returns that sDsSenha is missing (this parameter can hold a empty space).
Don't write &:
ParametrosConsulta.Values['sDsSenha'] := Edt_Sen.Text;
Ampersand are adding by Indy automatically. Btw, you may need to use TIdURI.PathEncode(Edt_Sen.Text) for escaping some characters.

How to access request headers in DataSnap Server?

I am using Delphi XE7. I need to access the request headers in DataSnap Server, but it does no seem to have this option. In DataSnap REST, it is possible, because TWebModule is available.
This code does not work, like in REST:
function TServerMethods1.EchoString(Value: string): string;
var
Module: TWebModule;
begin
Module := GetDataSnapWebModule;
Result := Module.Request.RemoteIP + ': ' + Value;
end;
Does anyone have an idea about DataSnap Server?
I update my project for REST, and resolved my problem in access Header. With the following code:
Need declare Web.HTTPApp at uses
var
oWebModule: TWebModule;
sHeader: String;
begin
oWebModule := GetDataSnapWebModule;
sHeader := oWebModule.Request.Content;
end;

Creating an amazon MWS signature with Delphi XE7 and Indy classes

I need to generate a signature for amazon MWS and decided to find a solution with only the components and classes which come with Delphi. Because I am using Indy for the HTTP post itself, it seemed to be a good idea to use Indy classes for the calculation of the RFC 2104-compliant HMAC.
For others, who work on amazon integration, the creation of the "Canonicalized Query String" is explained in the amazon tutorial very well: http://docs.developer.amazonservices.com/en_DE/dev_guide/DG_ClientLibraries.html
Be careful, just use #10 for line breaking, as #13#10 or #13 will fail with a wrong signature. It may also be important to add ":443" to the amazon Endpoint (Host), depending on the TIdHttp version, as explained in question #23573799.
To create a valid signature, we have to calculate a HMAC with SHA256 with the query string and the SecretKey we got from amazon after registration and then, the result has to be encoded in BASE64.
The query string is properly generated and identical to the string the amazon Scratchpad creates. But the call failed because the signature is not correct.
After some tests I realized that the signature I got from my query string is not the same as the result I got when I used PHP to generate it. The PHP result is considered as correct, as my PHP solution simply works with amazon since a long time, the Delphi result is different, which is not correct.
To make testing easier, I use '1234567890' as value for the query string and 'ABCDEFG' as replacement for the SecretKey. When the result I get with Delphi is the same as the result I get with PHP, the problem should be solved, I believe.
Here is how I get the correct result with PHP:
echo base64_encode(hash_hmac('sha256', '1234567890', 'ABCDEFG', TRUE));
This shows a result of
aRGlc3RY1pKmKX0hvorkVKNcPigiJX2rksqXzlAeCLg=
The following Delphi XE7 code returns the wrong result, while using the indy version that comes with Delphi XE7:
uses
  IdHash, IdHashSHA, IdHMACSHA1, IdSSLOpenSSL, IdGlobal, IdCoderMIME;
function GenerateSignature(const AData, AKey: string): string;
var
   AHMAC: TIdBytes;
begin
     IdSSLOpenSSL.LoadOpenSSLLibrary;
     With TIdHMACSHA256.Create do
      try
         Key:= ToBytes(AKey, IndyTextEncoding_UTF16LE);
         AHMAC:= HashValue(ToBytes(AData, IndyTextEncoding_UTF16LE));
         Result:= TIdEncoderMIME.EncodeBytes(AHMAC);
      finally
         Free;
      end;
end;
Here the result, which is shown in a Memo with
Memo.Lines.Text:= GenerateSignature('1234567890', 'ABCDEFG');
is:
jg6Oddxvv57fFdcCPXrqGWB9YD5rSvtmGnZWL0X+y0Y=
I believe the problem has something to do with the encodings, so I have done some research around that. As the amazon tutorial (link see above) tells, amazon expects a utf8 encoding.
As the Indy function "ToBytes" expect a string, which is a UnicodeString in my Delphi version, I quit testing with other string types as UTF8String for parameters or variables, but I just do not know where utf8 should come into place. Also I do not know if the encodings I use in the code above are the correct ones.
I choose UTF16LE because UnicodeString is utf16 encoded (see http://docwiki.embarcadero.com/RADStudio/Seattle/en/String_Types_(Delphi) for details) and LE (Little-Endian) is most commonly used on modern machines. Also the TEncodings of Delphi itself there is "Unicode" and "BigEndianUnicode", so "Unicode" seems to be LE and some kind of "standard" Unicode.
Of course I tested to use IndyTextEncoding_UTF8 instead of IndyTextEncoding_UTF16LE in the code above, but it does not work anyway.
Because
TIdEncoderMIME.EncodeBytes(AHMAC);
is writing the TidBytes to a Stream first and then reading it all with 8bit encoding, this could be a source of problem also, so I also tested with
Result:= BytesToString(AHMAC, IndyTextEncoding_UTF16LE);
Result:= TIdEncoderMIME.EncodeString(Result, IndyTextEncoding_UTF16LE);
but the result is the same.
If you like to see the main code for creating the request, here it is:
function TgboAmazon.MwsRequest(const AFolder, AVersion: string;
const AParams: TStringList; const AEndPoint: string): string;
var
i: Integer;
SL: TStringList;
AMethod, AHost, AURI, ARequest, AStrToSign, APath, ASignature: string;
AKey, AValue, AQuery: string;
AHTTP: TIdHTTP;
AStream, AResultStream: TStringStream;
begin
AMethod:= 'POST';
AHost:= AEndPoint;
AURI:= '/' + AFolder + '/' + AVersion;
AQuery:= '';
SL:= TStringList.Create;
try
SL.Assign(AParams);
SL.Values['AWSAccessKeyId']:= FAWSAccessKeyId;
SL.Values['SellerId']:= FSellerId;
FOR i:=0 TO FMarketplaceIds.Count-1 DO
begin
SL.Values['MarketplaceId.Id.' + IntToStr(i+1)]:= FMarketplaceIds[i];
end;
SL.Values['Timestamp']:= GenerateTimeStamp(Now);
SL.Values['SignatureMethod']:= 'HmacSHA256';
SL.Values['SignatureVersion']:= '2';
SL.Values['Version']:= AVersion;
FOR i:=0 TO SL.Count-1 DO
begin
AKey:= UrlEncode(SL.Names[i]);
AValue:= UrlEncode(SL.ValueFromIndex[i]);
SL[i]:= AKey + '=' + AValue;
end;
SortList(SL);
SL.Delimiter:= '&';
AQuery:= SL.DelimitedText;
AStrToSign:= AMethod + #10 + AHost + #10 + AURI + #10 + AQuery;
TgboUtil.ShowMessage(AStrToSign);
ASignature:= GenerateSignature(AStrToSign, FAWSSecretKey);
TgboUtil.ShowMessage(ASignature);
APath:= 'https://' + AHost + AURI + '?' + AQuery + '&Signature=' + Urlencode(ASignature);
TgboUtil.ShowMessage(APath);
finally
SL.Free;
end;
AHTTP:= TIdHTTP.Create(nil);
try
AHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(AHTTP);
AHTTP.Request.ContentType:= 'text/xml';
AHTTP.Request.Connection:= 'Close';
AHTTP.Request.CustomHeaders.Add('x-amazon-user-agent: MyApp/1.0 (Language=Delphi/XE7)');
AHTTP.HTTPOptions:= AHTTP.HTTPOptions + [hoKeepOrigProtocol];
AHTTP.ProtocolVersion:= pv1_0;
AStream:= TStringStream.Create;
AResultStream:= TStringStream.Create;
try
AHTTP.Post(APath, AStream, AResultStream);
Result:= AResultStream.DataString;
ShowMessage(Result);
finally
AStream.Free;
AResultStream.Free;
end;
finally
AHTTP.Free;
end;
end;
Urlencode and GenerateTimestamp are my own functions and they do what the name promises, SortList is my own procedure which sorts the stringlist in a byte order as requested by amazon, TgboUtil.ShowMessage is my own ShowMessage alternative which shows the complete message with all characters and is used for debugging only. The http protocol is 1.0 for testing only, because I got a 403 (permission denied) as HTTP return earlier. I just wanted to exclude this as problem as the indy documentation said, that protocol version 1.1 is considered incomplete because of problematic server answers.
There are several posts regarding the amazon mws topic here, but that specific problem seems to be new.
This question here may help someone who just not have come so far, but also I hope that someone can provide a solution to just get the same signature value in Delphi as I got with PHP.
Thank you in advance.
Using the latest SVN snapshot of Indy 10, I am not able to reproduce your signature problem. When using UTF-8, your example key+value data produces the same result in Delphi as the PHP output. So, your GenerateSignature() function is fine, provided that:
you use IndyTextEncoding_UTF8 instead of IndyTextEncoding_UTF16LE.
you make sure that AData and AKey contain valid input data.
Also, you should make sure that TIdHashSHA256.IsAvailable returns true, otherwise TIdHashHMACSHA256.HashValue() will fail.
this could happen, for instance, if OpenSSL fails to load.
Try this instead:
function GenerateSignature(const AData, AKey: string): string;
var
AHMAC: TIdBytes;
begin
IdSSLOpenSSL.LoadOpenSSLLibrary;
if not TIdHashSHA256.IsAvailable then
raise Exception.Create('SHA-256 hashing is not available!');
with TIdHMACSHA256.Create do
try
Key := IndyTextEncoding_UTF8.GetBytes(AKey);
AHMAC := HashValue(IndyTextEncoding_UTF8.GetBytes(AData));
finally
Free;
end;
Result := TIdEncoderMIME.EncodeBytes(AHMAC);
end;
That being said, there are quite a few problems with your MwsRequest() function:
you are leaking the TIdSSLIOHandlerSocketOpenSSL object. You are not assigning an Owner to it, and TIdHTTP does not take ownership when assigned to its IOHandler property. In fact, assigning the IOHanlder is actually optional in your example, see New HTTPS functionality for TIdHTTP for why.
you are setting AHTTP.Request.ContentType to the wrong media type. You are not sending XML data, so don't set the media type to 'text/xml'. In this situation, you need to set it to 'application/x-www-form-urlencoded' instead.
when calling AHTTP.Post(), your AStream stream is empty, so you are not actually posting any data to the server. You are putting your AQuery data in the query string of the URL itself, but it actually belongs in AStream instead. If you want to sent the data in the URL query string, you have to use TIdHTTP.Get() instead of TIdHTTP.Post(), and change your AMethod value to 'GET' instead of 'POST'.
you are using the version of TIdHTTP.Post() that fills an output TStream. You are using a TStringStream to convert the response to a String without any regard to the actual charset used by the response data. Since you are not specifying any TEncoding object in the TStringStream constructor, it will use TEncoding.Default for decoding, which may not (and likely will not) match the response's actual charset. You should instead use the other version of Post() that returns a String so TIdHTTP can decode the response data based on the actual charset reported by the HTTPS server.
Try something more like this instead:
function TgboAmazon.MwsRequest(const AFolder, AVersion: string;
const AParams: TStringList; const AEndPoint: string): string;
var
i: Integer;
SL: TStringList;
AMethod, AHost, AURI, AQuery, AStrToSign, APath, ASignature: string;
AHTTP: TIdHTTP;
begin
AMethod := 'POST';
AHost := AEndPoint;
AURI := '/' + AFolder + '/' + AVersion;
AQuery := '';
SL := TStringList.Create;
try
SL.Assign(AParams);
SL.Values['AWSAccessKeyId'] := FAWSAccessKeyId;
SL.Values['SellerId'] := FSellerId;
for i := 0 to FMarketplaceIds.Count-1 do
begin
SL.Values['MarketplaceId.Id.' + IntToStr(i+1)] := FMarketplaceIds[i];
end;
SL.Values['Timestamp'] := GenerateTimeStamp(Now);
SL.Values['SignatureMethod'] := 'HmacSHA256';
SL.Values['SignatureVersion'] := '2';
SL.Values['Version'] := AVersion;
SL.Values['Signature'] := '';
SortList(SL);
for i := 0 to SL.Count-1 do
SL[i] := UrlEncode(SL.Names[i]) + '=' + UrlEncode(SL.ValueFromIndex[i]);
SL.Delimiter := '&';
SL.QuoteChar := #0;
SL.StrictDelimiter := True;
AQuery := SL.DelimitedText;
finally
SL.Free;
end;
AStrToSign := AMethod + #10 + Lowercase(AHost) + #10 + AURI + #10 + AQuery;
TgboUtil.ShowMessage(AStrToSign);
ASignature := GenerateSignature(AStrToSign, FAWSSecretKey);
TgboUtil.ShowMessage(ASignature);
APath := 'https://' + AHost + AURI;
TgboUtil.ShowMessage(APath);
AHTTP := TIdHTTP.Create(nil);
try
// this is actually optional in this example...
AHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(AHTTP);
AHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
AHTTP.Request.Connection := 'close';
AHTTP.Request.UserAgent := 'MyApp/1.0 (Language=Delphi/XE7)';
AHTTP.Request.CustomHeaders.Values['x-amazon-user-agent'] := 'MyApp/1.0 (Language=Delphi/XE7)';
AHTTP.HTTPOptions := AHTTP.HTTPOptions + [hoKeepOrigProtocol];
AHTTP.ProtocolVersion := pv1_0;
AStream := TStringStream.Create(AQuery + '&Signature=' + Urlencode(ASignature);
try
Result := AHTTP.Post(APath, AStream);
ShowMessage(Result);
finally
AStream.Free;
end;
finally
AHTTP.Free;
end;
end;
However, since the response is documented as being XML, it would be better to return the response to the caller as a TStream (not using TStringStream, though) or TBytes instead of as a String. That way, instead of Indy decoding the bytes, let your XML parser decode the raw bytes on its own. XML has its own charset rules that are separate from HTTP, so let the XML parser do its job for you:
procedure TgboAmazon.MwsRequest(...; Response: TStream);
var
...
begin
...
AHTTP.Post(APath, AStream, Response);
...
end;

How to download ask.fm specific user wall source code with all questions and answers?

I'm trying to download all the questions and answers form users profile , but there is a problem , if user has big number of questions that I have to click on "Show more" to expand that list.If I try to download for example this persons questions and answers : http://ask.fm/UnaRamekic (random choice) , I'll get only those that are shown , those that are displayed after I click show more are not retrieved with get request.How can I get all the questions with ICS or Indy components. Thanks.
My code:
procedure TForm1.sButton1Click(Sender: TObject);
begin
With HttpCli1 do begin
URL := sedit1.Text;
RequestVer := '1.1';
RcvdStream := TMemoryStream.Create;
try
Get;
except
ShowMessage('There has been an error , check your internet connection !');
RcvdStream.Free;
Exit;
end;
RcvdStream.Seek(0,0);
Memo1.Lines.LoadFromStream(RcvdStream);
RcvdStream.Free;
end;
end;
Warning:
This approach is lame and quite dangerous. It's posting the form data similarly like the Show more button does, but it uses a while loop (to receive all pages), which repeats until the exact constant in response is found (in code it's the LastPageResponse constant), so when the response content of the page will be changed some time and that constant won't be in the response, you will find yourself in the infinite loop.
In the GetAllQuestions function you can specify:
AUser - is the user name after the slash from the URL
AFromDate - is a starting date time from which you want to get results
AStartPage - is a starting page from the AFromDate date time from which you want to get results
The GetAllQuestions function returns a base user's page, followed by line breaks separated content in a range from the base page to all pages from the time and page you specify. Forgot to notice, that the additional content you'll need to parse in a different way than a base page, since it's not a HTML content.
uses
IdHTTP;
implementation
function GetAllQuestions(const AUser: string; AFromDate: TDateTime;
AStartPage: Integer = 1): string;
var
Response: string;
LastPage: Integer;
TimeString: string;
HTTPClient: TIdHTTP;
Parameters: TStrings;
const
LineBreaks = sLineBreak + sLineBreak;
LastPageResponse = '$("#more-container").hide();';
begin
Result := '';
HTTPClient := TIdHTTP.Create(nil);
try
Result := HTTPClient.Get('http://ask.fm/' + AUser) + LineBreaks;
Parameters := TStringList.Create;
try
LastPage := AStartPage;
TimeString := FormatDateTime('ddd mmm dd hh:nn:ss UTC yyyy', AFromDate);
Parameters.Add('time=' + TimeString);
Parameters.Add('page=' + IntToStr(LastPage));
while LastPage <> -1 do
begin
Parameters[1] := 'page=' + IntToStr(LastPage);
Response := HTTPClient.Post('http://ask.fm/' + AUser + '/more',
Parameters);
if Copy(Response, Length(Response) - Length(LastPageResponse) + 1,
MaxInt) = LastPageResponse
then
LastPage := -1
else
LastPage := LastPage + 1;
Result := Result + Response + LineBreaks;
end;
finally
Parameters.Free;
end;
finally
HTTPClient.Free;
end;
end;
And the usage:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
Memo1.Text := GetAllQuestions('TLama', Now);
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
You're not going to be able to do that with Indy or ICS alone. What you initially see is exactly what is being downloaded when you pull the HTTP request.
If you look at the HTML source of the page, you'll see that the "View More" button has a JavaScript event handler attached to it that makes an AJAX request to the server, pulls more data from it, and applies it to the page. If you want to do the same, your code needs to parse things out at least enough to get the right AJAX parameters, then make the request to the server from your Indy or ICS code like any other HTTP request, and deal with the data that comes back.

Resources