I need to send PUT and DELETE along with POST, GET to a REST API how can I do it?
Delphi 7 comes with Indy. See the TIdHTTP component and specificly the Get and Put methods.
Or look at the open source Synapse library. There are some simple function calls in the HTTPSend unit which make implementing this completely painless. Just use the sample functions/procedures as your model for the PUT/DELETE. The existing routines already supply the POST and GET. The difference is in the method passed.
Personally I have found this library to be perfectly matched for working with REST. Its simple, well written and easy to extend.
For example, here is a simple put that sends and receives a stream:
function HttpPutBinary(const URL: string; const Data: TStream): Boolean;
var
HTTP: THTTPSend;
begin
HTTP := THTTPSend.Create;
try
HTTP.Document.CopyFrom(Data, 0);
HTTP.MimeType := 'Application/octet-stream';
Result := HTTP.HTTPMethod('PUT', URL); // changed method from 'POST'
Data.Size := 0;
if Result then
begin
Data.Seek(0, soFromBeginning);
Data.CopyFrom(HTTP.Document, 0);
end;
finally
HTTP.Free;
end;
end;
Check out the ICS components, they are suitable for the job.
Related
I am working with an API that provides me with the URL of a file and I wish to download the file to file or stream using Delphi XE.
The URL is in this form:-
https://xxxxxxx.s3-us-west-2.amazonaws.com/xxx/Reports/414_20160114021919.pdf?AWSAccessKeyId=xxxxxxxxxxxxxx&Expires=1478915858&response-content-disposition=attachment%3Bfilename%3D%22123%20Test%20St%20Dallas__2016_01_14_03_16_00.pdf%22&Signature=xxxxxxxxxxx%3D
where I have xxxxxxxxx'ed out the sensitive security stuff.
If I paste the URL into Chrome, it finds the file and brings up the save dialog.
In my Delphi Program I'm using URLMon with the following code (happily burgled and adapted from answers to similar questions here on SO) :-
function TdmXXXXXXXXX.DownloadFile(AURL, AExtWithDot: string): string;
// returns temp file name or empty string if failed;
var
sTempFileName: string;
iError: integer;
begin
sTempFileName := apmDM.GetTempFileName(AExtWithDot, True);
iError := UrlDownloadToFile(nil, PChar(AURL), PChar(sTempFileName), 0, nil);
if iError = S_OK then
Result := sTempFileName
else
Result := '';
showmessage(SysUtils.IntToHex(iError,8));
end;
The showmessage returns "800C000D"
In URLMon:-
$EXTERNALSYM INET_E_UNKNOWN_PROTOCOL}
INET_E_UNKNOWN_PROTOCOL = HResult($800C000D);
So I'm assuming that the problem relates to stuff after the "?" in the URL, "AWSAccessKeyId=" etc.
Not sure where to go from here - searched here and with Mr Google ...
Cheers
Jeff
Though you'll typically find pointers to the Indy TIdHTTP component, my personal favorite is including the Microsoft XML library (MSXML2_TLB), and using the XmlHTTPRequest component. If you use a TStreamAdapter on it's responseStream property, you'll have a stream to a resource over an URL.
I scratched all that I had done and restarted from scratch.
It all worked using URLMon and
URLDownloadToFile(nil, PChar(sReportURL), PChar(sTempFileName), 0, nil);
I have pored over the code and can't see any difference between reported problem code and my final code.
I want to display a 'news' page in a form using Deplhi TWebBrowser. The news page is a simple HTML page which we upload to our website from time to time and may be output from various tools. The display is fine but I'd like to know in my app whether it has changed since I last displayed it, so ideally I'd like to get either its modified date/time or its size / checksum. Precision is not important and ideally should not rely on properties that might fail because 'simple' tools were used to edit the HTML file such as NotePad. Checking on the web there are several document modified java calls but I really dont know where to start with those. I've looked through the numerous calls in Delphi's Winapi.WinInet unit and I see I can fetch the file with HTTP to examine it but that seems like cracking a walnut with a sledgehammer. I also cannot see any file date time functionality which makes me think I'm missing something obvious. I'm using Delphi XE5. In which direction should I be looking please? Thanks for any pointers.
You could use Indy TIdHTTP to send a HEAD request and examine Last-Modified / Content-Length headers.
e.g.:
procedure TForm1.Button1Click(Sender: TObject);
var
Url: string;
Http: TIdHTTP;
LastModified: TDateTime;
ContentLength: Integer;
begin
Url := 'http://yoursite.com/newspage.html';
Http := TIdHTTP.Create(nil);
try
Http.Head(Url);
LastModified := Http.Response.LastModified;
ContentLength := Http.Response.ContentLength;
ShowMessage(Format('Last-Modified: %s ; Content-Length: %d', [DateTimeToStr(LastModified), ContentLength]));
finally
Http.Free;
end;
end;
When the TWebBrowser.DocumentComplete event is fired make a HEAD request and store LastModified and ContentLength variables.
Then periodically make HEAD requests to test for changes (via TTimer for example).
These Header parameters are dependent on the web server implementation, and may not return file system date-time on the server (dynamic pages for example). your server might not result back these parameters at all.
For example, with static HTML pages on IIS, Last-Modified returns the file system last modified date-time, which is what you want.
For dynamic content (e.g. php, asp, .NET etc..), if you control the web-server, you might as well add your own custom HTTP response header on the server side to indicate the file system date-time (e.g. X-Last-Modified) or set the response Last-Modified header to your needs and examine this header on the client side.
If you need to examine/hash the entire HTTP content, you need to issue a GET method: http.Get(URL)
Thanks to a mixture of suggestions and pointers from kobik, David and TLama, I realised that I actually did need a sledgehammer and I finally came up with this solution (and I'm probably not the first, or the last!). I had to read the file contents because this did seem a better way of detecting changes. The code below calls "CheckForWebNewsOnTimer" from a TTimer infrequently and uses Indy to read the news page, make an MD5 hash of its contents and compare that with a previous hash stored in the registry. If the contents change, or 120 days elapses, the page pops up. The code has wrinkles, for example a change to a linked image on the page might not trigger a change but hey, its only news, and text almost always changes too.
function StreamToMD5HashHex( AStream : TStream ) : string;
// Creates an MD5 hash hex of this stream
var
idmd5 : TIdHashMessageDigest5;
begin
idmd5 := TIdHashMessageDigest5.Create;
try
result := idmd5.HashStreamAsHex( AStream );
finally
idmd5.Free;
end;
end;
function HTTPToMD5HashHex( const AURL : string ) : string;
var
HTTP : TidHTTP;
ST : TMemoryStream;
begin
HTTP := TidHTTP.Create( nil );
try
ST := TMemoryStream.Create;
try
HTTP.Get( AURL, ST );
Result := StreamToMD5HashHex( ST );
finally
ST.Free;
end;
finally
HTTP.Free;
end;
end;
function ShouldShowNews( const ANewHash : string; AShowAfterDays : integer ) : boolean;
const
Section = 'NewsPrompt';
IDHash = 'LastHash';
IDLastDayNum = 'LastDayNum';
var
sLastHash : string;
iLastPromptDay : integer;
begin
// Check hash
sLastHash := ReadRegKeyUserStr( Section, IDHash, '' );
Result := not SameText( sLastHash, ANewHash );
if not Result then
begin
// Check elapsed days
iLastPromptDay := ReadRegKeyUserInt( Section, IDLastDayNum, 0 );
Result := Round( Now ) - iLastPromptDay > AShowAfterDays;
end;
if Result then
begin
// Save params for checking next time.
WriteRegKeyUserStr( Section, IDHash, ANewHash );
WriteRegKeyUserInt( Section, IDLastDayNum, Round(Now) );
end;
end;
procedure CheckForWebNewsOnTimer;
var
sHashHex, S : string;
begin
try
S := GetNewsURL; // < my news address
sHashHex := HTTPToMD5HashHex( S );
If ShouldShowNews( sHashHex, 120 {days default} ) then
begin
WebBrowserDlg( S );
end;
except
// .. ignore or save as info
end;
end;
On my program I have a function that checks for the current version of the program, which it gets from the url: www.tablemaster.webs.com/versioninfo.txt
As you can see, at the URL is just plain text. I need to load this text from the URL into a string on my program, how will I go about doing this? I've searched around but found nothing..
PS: I need the simplest code possible..
Thanks in advance :)
I would use Indy's TIdHTTP with it's easiest GET overload this way:
uses
IdHTTP;
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
IdHTTP: TIdHTTP;
begin
IdHTTP := TIdHTTP.Create(nil);
try
S := IdHTTP.Get('http://www.tablemaster.webs.com/versioninfo.txt');
ShowMessage(S);
finally
IdHTTP.Free;
end;
end;
you can use TIEHTTP component from myfxboard..
to load txt file from url with TIEHTTP:
http.ExecuteURL('www.tablemaster.webs.com/versioninfo.txt');
Memo1.Lines.Add(http.sl.Text);
I would use MSXML2_TLB, especially if I already use XML in the project. MSXML2 is present in Windows since version Internet Explorer 5.5, so chances are real good it's present on the system. You can get MSXML2_TLB.pas using the Import Type Library option from the main menu, and selecting "Microsoft XML, v6.0" (or higher) from the list.
var
r:XMLHTTP;
begin
r:=CoXMLHTTP.Create;
r.open('GET','http://www.tablemaster.webs.com/versioninfo.txt','','');
r.send(EmptyParam);
if r.status<>200 then raise Exception.Create(IntToStr(r.status)+' '+r.statusText);
Result:=r.responseText;
end;
uses
IdHTTP;
procedure TForm1.Button1Click(Sender: TObject);
var
.
.
it seems in this method system cached the txt file so if the file change on web this method denies changes.!
This is an attempt to rephrase my previous question as a result of the feeedback which it received.
I want a simple network communication which I can use as an underlying framework and never have to look at again. I just want o push a string from one PC to another and get a string in response. I don't want to have to worry about opening conenctions, keeping them open, reopening them if they close, etc.
I want to concentrate on my application and have a simple functional API along the lines of:
SendStringToOtherPc() : String; // Called at PC #1.
// Returns PC #2's result string
// or "" on error (or throws exception)
ProcessReceivedStringAndReply(); // Called at PC # 2. Sends result string
I do need to know if the other PC replied or not; and, if so, what the result string was
also "nice to have" would be for both PCs to initiate communication. If not, I can have one of them (the client poll), or have the other send its communication as a reply to the heartbeat which I need to add.
I presume that those with multiple fprojects under their belts have a "starter" framework which they use for every new project, just adding the application specific log - and it's such a framwork, or abstraction layer, that I want. Can anyone point me at a URL?
I know nothing of socket programming and don't really have time to learn. If I do, some other project will suffer.
While I do respect the argument that I should understand what my software is doing, there is a valid counter-arguement that everyone should not have to develop this particular wheel for himself, and surely there is some FOSS around which does what I want?
Thanks in advance.
Update: I seem to have started a little controversy, with some thinking me lazy or doomed to disaster. So, maybe I should explain a little of my history.
I spent three decades developing telecoms software and we always followed the OSI 7 layer model. I was generally layer 3, the network layer, and no matter whether it was a telephone exchange, base station or hanset, whether the protocol was ISDN, ISUP, DECT, GSM, GPRS, UMTS or a propietary satellite protocol, I could always instuct a Serveice Access Point of Layer 2, the data transport layer, "hey, you! Get this mesage to the other guy and tell me what his reply is". Did I know how it was done? Did I care?
#CosmicPrund, who will probably be awarded the answer unless someone points me at a Layer 2, said "The true answer to this question is that all you need is learn how to use Indy" and I beg to disagree.
Someone will, but not me if I can help it. I already leanred too many skills, programming languages, databse systems, oprerating systems and will always avoid learning more that an overview of another if I can. Like Sir Isaac Newton, I would prefer to stand on the shoulders of giants.
Software is just getting too big for one guy. Surely none of you start each project from scratch? I guess you reuse the networking code from a previous project(?) and that reusable code is my "Layer 2". And my question is where can I download such code and use it without understanding its inner workings?
Does anyone know of such a thing?
Answer: I used Indy and got what I wanted. I will porbably try to build up a library of functions which I can use as a network abstraction layer.
I have a free framework that will do all this. The benefit is that you can use it without any knowledge of sockets whatsoever. You can safely ignore connects and disconnects because this is all handled by the framework (the underlying comms framework keeps a continuous connection via configurable pings, etc). A message queueing threading model is also built into the framework. I have a demo for your exact example as well. The downside is obviously a steep learning curve. Have a look at http://www.csinnovations.com/framework_delphi.htm
The true answer to this question is that all you need is learn how to use Indy. To prove my point I'll give you a 89 lines unit that actually implements all you requested, plus a proof-of-concept sample of how to use it.
Before I show the code I'd like to mention:
89 lines of code can't be called a framework. It's just a thin wrapper that's simply not worth it. Sooner or later you'd run into stuff that requires direct access to the underlying Indy framework.
Someone with more Indy experience would probably write this using even less lines of code.
I could even make it shorter myself, since I included two overloaded "StartServer" methods for ease of demonstration.
Implementing this using components dropped on a form would cut the number of lines further.
Here's the "framework" unit:
unit UTcpIntercom;
interface
uses IdContext, IdCustomTCPServer, IdTCPServer, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, SysUtils;
type
EIntercomError = class(Exception);
TReceivedText = procedure(const TextFromClient:string; var Response:string) of object;
TReceivedTextProc = procedure(const TextFromClient:string; var Response:string);
TIntercomServer = class(TIdCustomTCPServer)
protected
Event: TReceivedText;
Proc: TReceivedTextProc;
HostGreeting: string;
public
function DoExecute(AContext: TIdContext): Boolean; override;
end;
function SendTextToComputer(const TextToSend, HostToSend, HostGreeting:string; PortNumber: Integer): string;
function StartServer(PortNumber:Integer; const HostGreeting:string; OnReceivedText: TReceivedText):TIntercomServer;overload;
function StartServer(PortNumber:Integer; const HostGreeting:string; OnReceivedText: TReceivedTextProc):TIntercomServer;overload;
implementation
function SendTextToComputer(const TextToSend, HostToSend, HostGreeting:string; PortNumber: Integer): string;
var Id: TIdTCPClient;
begin
Id := TIdTCPClient.Create(nil);
try
Id.Host := HostToSend;
Id.Port := PortNumber;
Id.Connect;
try
if Id.IOHandler.ReadLn <> HostGreeting then
raise EIntercomError.Create('Host is invalid: ' + HostToSend);
Id.IOHandler.WriteLn(TextToSend);
Result := Id.IOHandler.ReadLn;
Id.Disconnect;
finally Id.Disconnect;
end;
finally Id.Free;
end;
end;
function StartServer(PortNumber:Integer; const HostGreeting:string; OnReceivedText: TReceivedText):TIntercomServer;overload;
begin
Result := TIntercomServer.Create(nil);
Result.Bindings.Add.Port := PortNumber;
Result.HostGreeting := HostGreeting;
Result.Event := OnReceivedText;
Result.Active := True;
end;
function StartServer(PortNumber:Integer; const HostGreeting:string; OnReceivedText: TReceivedTextProc):TIntercomServer;overload;
begin
Result := TIntercomServer.Create(nil);
Result.Bindings.Add.Port := PortNumber;
Result.HostGreeting := HostGreeting;
Result.Proc := OnReceivedText;
Result.Active := True;
end;
{ TIntercomServer }
function TIntercomServer.DoExecute(AContext: TIdContext): Boolean;
var Text, Response: string;
begin
AContext.Connection.IOHandler.WriteLn(HostGreeting);
Text := AContext.Connection.IOHandler.ReadLn;
Response := '';
if Assigned(Event) then
Event(Text, Response)
else if Assigned(Proc) then
Proc(Text, Response)
else
Response := 'No handler assigned.';
AContext.Connection.IOHandler.WriteLn(Response);
AContext.Connection.Disconnect;
Result := True;
end;
end.
Here's the code that uses the unit. Notice the DoSomethingWithTextFromClient, that's essentially your ProcessReceivedStringAndReply method. Also notice the use of StartServer and SendTextToComputer.
program Project9;
{$APPTYPE CONSOLE}
uses
SysUtils,
UTcpIntercom in 'UTcpIntercom.pas';
procedure DoSomethingWithTextFromClient(const TextFromClient: string; var Response:string);
var i: Integer;
C: Char;
Len: Integer;
begin
Response := TextFromClient;
Len := Length(Response);
for i:=1 to (Length(Response) div 2) do
begin
C := Response[Len-i+1];
Response[Len-i+1] := Response[i];
Response[i] := C;
end;
end;
begin
try
try
with StartServer(1000, 'Test', #DoSomethingWithTextFromClient) do
begin
WriteLn(SendTextToComputer('12345678', '127.0.0.1', 'Test', 1000));
Free;
end;
Readln;
except on E:Exception do
begin
WriteLn(E.ClassName);
WriteLn(E.Message);
Readln;
end;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Here is my little code:
curMessage:TIdMessage;
tidImap: TIdIMAP4;
...
tidImap.UIDRetrieve('123', curMessage);
That works fine! Now when i try to read
curMessage.Body
Then it is empty sometimes. I've understand that it is empty when message IsMsgSinglePartMime is False. So then i can't read message's body from Body property.
I've searched in curMessage's every property, but nowhere could i found the body text. What makes it even more odd, is that when i save curMessage
curMessage.Savefile('...');
then i can see all the body there.
I don't want to make another request to fetch for the body (eg UIDRetrieveText(2)) because i understand that the body data is there somewhere, i just could not find it or is Savefile/SaveStream making some internal requests to server?
Thank you guys in advance!
You need to be checking TIdMessage.MessageParts.
var
Msg: TIdMessage;
i: Integer;
begin
// Code to retrieve message from server
for i := to Msg.MessageParts.Count - 1 do
begin
if (Msg.MessageParts.Items[i] is TIdAttachment) then
// Handle attachment
else
begin
if Msg.MessageParts.Items[i] is TIdText then
HandleText(TIdText(Msg.MessageParts.Items[i]).Body);
end;
end;
end;
In Indy 10, TIdMessageParts has been moved into it's own unit, so you may have to add IdMessageParts to your uses clause.