15

I am trying to make HTTP Requests from Delphi using the WinInet functions.

So far I have:

function request:string; var hNet,hURL,hRequest: HINTERNET; begin hNet := InternetOpen(PChar('User Agent'),INTERNET_OPEN_TYPE_PRECONFIG or INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); if Assigned(hNet) then begin try hURL := InternetConnect(hNet,PChar('http://example.com'),INTERNET_DEFAULT_HTTP_PORT,nil,nil,INTERNET_SERVICE_HTTP,0,DWORD(0)); if(hURL<>nil) then hRequest := HttpOpenRequest(hURL, 'POST', PChar('param=value'),'HTTP/1.0',PChar(''), nil, INTERNET_FLAG_RELOAD or INTERNET_FLAG_PRAGMA_NOCACHE,0); if(hRequest<>nil) then HttpSendRequest(hRequest, nil, 0, nil, 0); InternetCloseHandle(hNet); except on E : Exception do ShowMessage(E.ClassName+' error raised, with message : '+E.Message); end; end end; 

But this doesn't do anything (I am sniffing network http traffic to see if it works). I have successfully used InternetOpenURL but I also need to send POST request and that function doesn't do that.

Could someone show me a simple example? The result I want is to get the http response page in a var as string.

4 Answers 4

9

I got all the url/filename part messed up with the previous code. I'm using this from Jeff DeVore now and it's working fine:

function request(const AUrl, AData: AnsiString; blnSSL: Boolean = True): AnsiString; var aBuffer : Array[0..4096] of Char; Header : TStringStream; BufStream : TMemoryStream; sMethod : AnsiString; BytesRead : Cardinal; pSession : HINTERNET; pConnection : HINTERNET; pRequest : HINTERNET; parsedURL : TStringArray; port : Integer; flags : DWord; begin ParsedUrl := ParseUrl(AUrl); Result := ''; pSession := InternetOpen(nil, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); if Assigned(pSession) then try if blnSSL then Port := INTERNET_DEFAULT_HTTPS_PORT else Port := INTERNET_DEFAULT_HTTP_PORT; pConnection := InternetConnect(pSession, PChar(ParsedUrl[0]), port, nil, nil, INTERNET_SERVICE_HTTP, 0, 0); if Assigned(pConnection) then try if (AData = '') then sMethod := 'GET' else sMethod := 'POST'; if blnSSL then flags := INTERNET_FLAG_SECURE or INTERNET_FLAG_KEEP_CONNECTION else flags := INTERNET_SERVICE_HTTP; pRequest := HTTPOpenRequest(pConnection, PChar(sMethod), PChar(ParsedUrl[1]), nil, nil, nil, flags, 0); if Assigned(pRequest) then try Header := TStringStream.Create(''); try with Header do begin WriteString('Host: ' + ParsedUrl[0] + sLineBreak); WriteString('User-Agent: Custom program 1.0'+SLineBreak); WriteString('Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'+SLineBreak); WriteString('Accept-Language: en-us,en;q=0.5' + SLineBreak); WriteString('Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7'+SLineBreak); WriteString('Keep-Alive: 300'+ SLineBreak); WriteString('Connection: keep-alive'+ SlineBreak+SLineBreak); end; HttpAddRequestHeaders(pRequest, PChar(Header.DataString), Length(Header.DataString), HTTP_ADDREQ_FLAG_ADD); if HTTPSendRequest(pRequest, nil, 0, Pointer(AData), Length(AData)) then begin BufStream := TMemoryStream.Create; try while InternetReadFile(pRequest, @aBuffer, SizeOf(aBuffer), BytesRead) do begin if (BytesRead = 0) then Break; BufStream.Write(aBuffer, BytesRead); end; aBuffer[0] := #0; BufStream.Write(aBuffer, 1); Result := PChar(BufStream.Memory); finally BufStream.Free; end; end; finally Header.Free; end; finally InternetCloseHandle(pRequest); end; finally InternetCloseHandle(pConnection); end; finally InternetCloseHandle(pSession); end; end; 

ParseUrl is a function that splits a URL in "hostname / filename" and TStringArray is an array of strings. I still have to review the code tomorrow but it looks fine and in my sniffer I saw the post data and headers being sent.

Sign up to request clarification or add additional context in comments.

3 Comments

I found the source of your code for you so you can cite the original author. I changed the code to remove the ill-conceived Boolean case statements, and I changed the referrer so it doesn't lie and claim to be Firefox 3.0.10.
I can't find what unit contains ParseURL, or maybe its a custom routine. The code at: delphi.pastebin.com/f1ea3a752 is somewhat different and doen't use ParseURL.
How would you add a custom header value to the post request?
2

Personally I prefer to use the synapse library for all of my TCP/IP work. For example, a simple HTTP post can be coded as:

uses httpsend; function testpost; begin stm := tStringstream.create('param=value'); try HttpPostBinary('http://example.com',Stm); finally stm.free; end; end; 

The library is well written and very easy to modify to suit your specific requirements. The latest subversion release works without any problems for both Delphi 2009 and Delphi 2010. This framework is not component based, but rather is a series of classes and procedures which well in a multi-threaded environment.

Comments

1

The third parameter (lpszObjectName) to HttpOpenRequest should be the URL you wish to request. That's why the documentation describes the fifth parameter (lpszReferer) as "a pointer to a null-terminated string that specifies the URL of the document from which the URL in the request (lpszObjectName) was obtained."

The posted data gets sent with HttpSendRequest; the lpOptional parameter is described like this:

Pointer to a buffer containing any optional data to be sent immediately after the request headers. This parameter is generally used for POST and PUT operations. The optional data can be the resource or information being posted to the server. This parameter can be NULL if there is no optional data to send.

The second parameter to InternetOpen should be just the server name; it should not include the protocol. The protocol you specify with the sixth parameter.

After you've sent the request, you can read the response with InternetReadFile and InternetQueryDataAvailable.

Don't just check whether the API functions return zero and then proceed on the next line. If they fail, call GetLastError to find out why. The code you've posted will not raise exceptions, so it's futile to catch any. (And it's foolish to "handle" them the way you're doing so anyway. Don't catch an exception that you don't already know how to fix. Let everything else go up to the caller, or the caller's caller, etc.)

Comments

1

You can use a late binding with an OleObject and OleVariants of :WinHttp.WinHttpRequest

https://api-ninjas.com/api/textsimilarity

function TextSimilarity_API6(AURL, url_name, aApikey: string): string; var httpReq,hr: Olevariant; JPostdatabody: string; jo: TJSON; begin Result:= ''; JPostDataBody:= '{' + '"text_1": "gpt-3.5-turbo-instruct",'+ //'"prompt": "%s",'+ '"text_2": "gpt-3.5-turbo-instruct777"'+ '}'; // Use JSON for REST API calls and set API KEY via requestheader httpReq:= CreateOleObject('WinHttp.WinHttpRequest.5.1'); jo:= TJSON.Create(); try hr:= httpReq.Open('POST', AURL, false); httpReq.setRequestheader('user-agent',SUSERAGENT); // Select HTTPS POST method, set POST data, specify endpoint URL httpReq.setRequestheader('content-type','application/json'); httpReq.setRequestheader('X-Api-Key',aApikey); if hr= S_OK then HttpReq.Send(JPostDataBody); If HttpReq.Status = 200 Then result:= HttpReq.responseText Else result:= 'Failed getresponse:'+itoa(HttpReq.Status)+HttpReq.responseText; // writeln('debug response '+HttpReq.GetAllResponseHeaders); // Process returned JSON when request was successful jo.parse(result) result:='Answer2similarity: '+jo.values['similarity'].asstring; finally jo.Free; httpreq:= unassigned; end; end; 

2 Comments

Great Help for Scripting
Will the HTTP body be sent UTF-8 encoded?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.