I am attempting to convert some code from TWebBrowser to Chromium but am having trouble figuring out how to send post and header data with an HTTP request.
Below is the TWebBrowser functionality I'm trying to implement.
var
VHeader, PostData: OleVariant;
PostData := VarArrayCreate([0, Length(XMLString) - 1], varByte) ;
HeaderData := 'Content-Type: application/x-www-form-urlencoded'+ '\n';
WebBrowser1.Navigate(StrUrl,EmptyParam,EmptyParam,PostData,VHeader);
How do I do the equivalent with Chromium?
Due to a missing documentation for Delphi Chromium Embedded, I'll refer the needed requirements for sending web requests for the C++ version of CEF. So, you need to use the LoadRequest method for sending requests in Chromium. For using it, you need the object instance of the CefRequest request object class along with the HeaderMap and CefPostData objects for request header and data specification.
Expanding on Henri Gourvest's (author of the Delphi CEF wrapper) example from this thread, you can in Delphi try something like the following pseudo-code:
uses
ceflib;
function CreateField(const AValue: AnsiString): ICefPostDataElement;
begin
Result := TCefPostDataElementRef.New;
Result.SetToBytes(Length(AValue), PAnsiChar(AValue));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Header: ICefStringMultimap;
Data: ICefPostData;
Request: ICefRequest;
begin
Header := TCefStringMultimapOwn.Create;
Header.Append('Content-Type', 'application/x-www-form-urlencoded');
Data := TCefPostDataRef.New;
Data.AddElement(CreateField('Data.id=27'));
Data.AddElement(CreateField('&Data.title=title'));
Data.AddElement(CreateField('&Data.body=body'));
Request := TCefRequestRef.New;
Request.Flags := WUR_FLAG_NONE;
Request.Assign('http://example.com/', 'POST', Data, Header);
Chromium1.Browser.MainFrame.LoadRequest(Request);
end;
The same should do another version of the above code:
procedure TForm1.Button1Click(Sender: TObject);
var
Header: ICefStringMultimap;
Data: ICefPostData;
Request: ICefRequest;
begin
Request := TCefRequestRef.New;
Request.Url := 'http://example.com/';
Request.Method := 'POST';
Request.Flags := WUR_FLAG_NONE;
Header := TCefStringMultimapOwn.Create;
Header.Append('Content-Type', 'application/x-www-form-urlencoded');
Request.SetHeaderMap(Header);
Data := TCefPostDataRef.New;
Data.AddElement(CreateField('Data.id=27'));
Data.AddElement(CreateField('&Data.title=title'));
Data.AddElement(CreateField('&Data.body=body'));
Request.PostData := Data;
Chromium1.Browser.MainFrame.LoadRequest(Request);
end;
Related
in delphi 10 with the Datasnap component I am trying to declare a Post method that receives an XML file but I can't.
Does anybody know if Datasnap only can receive Json format type in the body?
(in contrary any example will be great)
Thanks in advance.
You can overcome this with your datasnap WebModuleUnit and your custom ufileUploader unit like this :
In WebModuleUnit :
procedure TWebModule1.WebModuleDefaultAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
if Request.InternalPathInfo.StartsWith('/UploadFile') then
Response.Content := ufileUploader.UploadFile(Request)
else if (Request.InternalPathInfo = '') or (Request.InternalPathInfo = '/')
then
Response.Content := ReverseString.Content
else
Response.SendRedirect(Request.InternalScriptName + '/');
end;
in your ufileUploader unit :
unit ufileUploader;
interface
uses Web.HTTPApp;
function UploadFile(ARequest: TWebRequest): string;
implementation
uses System.SysUtils, System.Classes, Web.ReqMulti;
function UploadFile(ARequest: TWebRequest): string;
const
DestPath = 'c:\';
var
i: integer;
LFileName: string;
LStream: TMemoryStream;
begin
if not TMultipartContentParser.CanParse(ARequest) then
begin
Result := 'Cannot parse request';
Exit;
end;
if ARequest.Files.Count < 1 then
begin
Result := 'No file sended';
Exit;
end;
LStream := TMemoryStream.Create;
try
// You have sended ARequest.Files.Count files
for i := 0 to ARequest.Files.Count - 1 do
begin
LFileName := string(ARequest.Files.Items[i].FileName);
LStream.Clear;
// Read the sended file stream
LStream.CopyFrom(ARequest.Files.Items[i].Stream,
ARequest.Files.Items[i].Stream.Size);
LStream.Position := 0;
// Do what you want with the stream
LStream.SaveToFile(DestPath + LFileName);
end;
Result := Format('%d files saved to %s ', [ARequest.Files.Count, DestPath]);
finally
FreeAndNil(LStream);
end;
end;
end.
This is not the answer you are hoping for, but I opened a case with Embarcadero for this exact problem and their response was:
Hello
My name is Steve Axtell. I am looking at this case.
I am sorry but Datasnap does not support XML. It only supports JSON,
hence the error message.
Regards
Steve Axtell Embarcadero Support ref:_00D30HwR._5005a28Y6yq:ref
The problem is in Datasnap.DSService.TDSRESTService.ProcessParameters:
// Look for more parameters in the body
if (Content <> nil) and (Length(Content) > 0) then
begin
if LBody = nil then
begin
LBodyArray := nil;
LBody := TJSONObject.ParseJSONValue(Content, 0);
LFreeBody := LBody;
if LBody = nil then
begin
//ParamArray.Free;
raise TDSServiceException.Create(SNoJSONValue);
end;
if (LBody is TJSONObject) and (TJSONObject(LBody).Count = 1) and
(TJSONObject(LBody).Pairs[0].JSonString.Value = PARAMETERS_PAIR) and
(TJSONObject(LBody).Pairs[0].JsonValue is TJSONArray) then
begin
LBodyArray := TJSONArray(TJSONObject(LBody).Pairs[0].JsonValue);
LBodyArrayIndex := 0;
end
end;
end;
If the body is not in JSON, it fails to process the REST request, and I've not found a way to force DataSnap to not look in the body for additional parameters.
Note that in my case, I'm not using TComponent but TDataModule for the server methods.
I am trying to address the OriginStamp.org site to post a file hash and to get its timestamp
curl -X GET -H "Content-Type: application/json" -H "Authorization: xxxxxxxxxxxxxxxxxxx" http://api.originstamp.org/api/yyyyyyyyyyy
I used various formats to access this API but this seemed to be most hopeful but with out success
PROCEDURE TInterNetFm.GetTIMESTAMP;
var
i : integer;
response : string;
JSONToSend : TStringStream;
IndyH : TIdHTTP;
begin
IndyH := TIdHTTP.Create(Application);
JSONToSend := TStringStream.Create('{}');
IndyH.Request.Connection := 'Keep-Alive';
IndyH.Request.CustomHeaders.Clear;
IndyH.Request.CustomHeaders.Values['Content-Type'] := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization']:='xxxxxxxxxxxxxxx';
IndyH.Request.ContentType := 'application/json';
response:=IdHttp1.post('http://originstamp.org/api/
05c2422f44ddd24ba3f25848773a8fcb48435f8f966381da4732c40a7255780c',
JSONToSend);
JSONToSend.free;
IndyH.free;
end;
This gives a HTTP/1.1 error 403 forbidden.
I have also tried IdHttp1.getand the Delphi REST debugger and various attampts at REST.
Any suggestions about how I am going wrong with this would be grately appreciated.
A 403 error implies that you are likely using an invalid API key in the Authorization header. Also, make sure that Request.BasicAuthentication is set to False so TIdHTTP doesn't try to send its own Authorization header.
That being said, I see several other issues with your code.
You are leaking memory if TIdHTTP.Post() raises an exception on failure.
You are sending 2 Content-Type headers (one from Request.ContentType and the other from CustomerHeaders.Values['Content-Type']). Use Request.ContentType only.
You are sending your request to originstamp.org when you should be sending it to api.originstamp.org instead.
You are creating and configuring a TIdHTTP object named IndyH, but you are performing the actual Post() using another object named IdHttp1 instead.
Try this:
function TInterNetFm.GetTIMESTAMP(const AHash: string): string;
var
IndyH : TIdHTTP;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
Response := IndyH.Get('http://api.originstamp.org/api/' + AHash);
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;
function TInterNetFm.GetTIMESTAMP(const AHash, AData: string): string;
var
IndyH : TIdHTTP;
JSONToSend : TStringStream;
Response : string;
begin
IndyH := TIdHTTP.Create(nil);
try
IndyH.Request.BasicAuthentication := False;
IndyH.Request.ContentType := 'application/json';
IndyH.Request.CustomHeaders.Values['Authorization'] := 'YOUR_API_KEY';
JSONToSend := TStringStream.Create(AData, TEncoding.UTF8);
try
Response := IndyH.Post('http://api.originstamp.org/api/' + AHash, JSONToSend);
finally
JSONToSend.Free;
end;
finally
IndyH.Free;
end;
// parse Response JSON as needed...
Result := ...;
end;
I can use the following code to easily get the HTML source from the URL, but how can I get the actual URL itself? Because sometimes the initial URL goes through some redirects and the actual URL is not the same and I would like to capture it for usage. I cannot seem to find any good documentation on the usage of methods or properties for winHTTP in Delphi. Thanks!
var http: variant;
begin
http:=createoleobject('WinHttp.WinHttpRequest.5.1');
http.open('GET', 'http://URLtoWebsite.com', false);
http.send;
showmessage(http.responsetext);
end;
You can use something like this
function GetFinalURL(const AMainURL: string): string;
var
http: Variant;
begin
Result := '';
http := CreateOleObject('WinHttp.WinHttpRequest.5.1');
http.Option(6) := False;
http.open('GET', AMainURL, false);
http.send;
if http.Status = 302 then
Result := http.getResponseHeader('Location')
else
Result := AMainURL;
end;
Another way using Indy
function GetFinalURL(const AMainURL: string): string;
var
idHTTP: TIdHTTP;
begin
Result := '';
idHTTP := TIdHTTP.Create(nil);
try
idHTTP.HandleRedirects := True;
try
idHTTP.Get(AMainURL);
Result := idHTTP.Request.URL;
except
end;
finally
idHTTP.Free;
end;
end;
You can set WinHttpSetStatusCallback with WINHTTP_CALLBACK_FLAG_REDIRECT parameter to receive notifications about every redirect occurred during request.
I am trying to get all the raw request headers from Asynchronous Pluggable Protocol I've implemented. But I can only get a few basic headers using IHttpNegotiate. Such as Accept-Language, Referer. With a tool called HTTP Analyzer these things can be viewed in more detail.
function RetrieveRequestHeaders(const szUrl: PWideChar; const OIProtSink: IInternetProtocolSink): String;
var
pHttpNeg: IHttpNegotiate;
Headers: PWideChar;
HR: HResult;
begin
Result := '';
HR := IUnknown_QueryService(OIProtSink, IID_IHttpNegotiate, IID_IHttpNegotiate, pHttpNeg);
if Succeeded(HR) then
begin
Headers := nil;
HR := pHttpNeg.BeginningTransaction(szUrl, nil, 0, Headers);
if Succeeded(HR) then
begin
Result := Headers;
CoTaskMemFree(Headers);
end;
end;
end;
IHTTPNegotiate.BeginningTransaction will give you the (additional) headers the browser wants to add to the outgoing request. As the protocol handler you're responsible to create the full outgoing HTTP request header, like you can see with a HTTP analyzer tool.
I implemented same code (to post a form) using delphi and python. The python code works perfectly, but delphi code fails. In python, I can simply write httplib2.debuglevel=4 to see what content has actually been sent to the server. but I have no idea how to print the content in delphi.
def python_request_data(url, cookie, data):
httplib2.debuglevel = 4
conn = httplib2.Http()
conn.follow_all_redirects = True
headers = {'Cookie': cookie, 'Content-Type': 'application/x-www-form-urlencoded'}
response, contents = conn.request(url, 'POST', data, headers=headers)
procedure DelphiRequestData(const Url, Cookie, Data: string);
var
Client: TIdHttp;
Params: TStringList;
Response: string;
begin
Client := TIdHttp.Create(nil);
try
Client.HTTPOptions := [hoKeepOrigProtocol];
Client.Request.CustomHeaders.AddValue('Cookie', Cookie);
Params := TStringList.Create;
try
Params.QuoteChar := #0;
Params.Delimiter := '&';
Params.DelimiterText := Data;
Client.Request.ContentType := 'application/x-www-form-urlencoded';
Client.Request.ContentLength := Length(Params.DelimitedText);
Response := Client.Post(Url, Params);
finally
Params.Free;
end;
finally
Client.Free;
end;
end;
Any hints are appreciated.
You ca use TIdLogDebug as Intercept of your IdHttp.
The Events OnSend and OnReceive will deliver the desired Informations in a Array or TBytes.