I'm currently building a restful API server for my mobile devices to access a mySQL database. The mobile apps need to be able to get data from the server, and also insert and edit data.
I have managed to successfuly do a GET from the server, but I'm stuck at how to get do the post. On the WebModuleUnit I added an action, with the following properties:
MethodType mtGet
Name actImsDocuments
PathInfo /imsDocuments
procedure TWebModule1.WebModule1actImsDocumentsAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
qryDocuments: TFDQuery;
JsonArray: TJSONArray;
JsonObject: TJSONObject;
begin
// Configure response
Response.ContentType := 'application/json; charset=utf-8';
//Search for my clients table
qryDocuments := TFDQuery.Create(nil);
with qryDocuments do
begin
Connection := FDConnection1;
Active := false;
SQL.Clear;
Open('SELECT * FROM ims_document');
if qryDocuments.RecordCount > 0 then //if there is items
begin
JsonArray := TJSONArray.Create;
try
First;
while not EoF do //Iterate all items in the FDQuery
begin
JsonObject := TJSONObject.Create;
AddFieldsToJSON(JsonObject, qryDocuments);
JsonArray.AddElement(JsonObject);
Next;
end;
finally
Response.Content := JsonArray.ToString;
JsonArray.DisposeOf;
end;
end;
end;
end;
procedure TWebModule1.AddFieldsToJSON(AJsonObject: TJSONObject;
AQuery: TFDQuery);
begin
with AQuery do
begin
AJsonObject.AddPair('number', TJSONNumber.Create(FieldByName('number').AsInteger));
AJsonObject.AddPair('document_title', FieldByName('document_title').AsString);
end;
end;
Now this all above works perfectly, what I have no idea how to do, is how do I for example, edit a record in the table, or insert a new record, how do I get the values from the mobile device. I have no idea how to do a post, or update. Can anyone just give me an example of code please, as I have looked everywhere and just cannot get a clear understanding of this.
If I change the property of the action to mtPost, how would my code differ If I wanted to add a record to the table?
Related
I'm having trouble with a POST request to an API which I am not the owner of.
The request must simply post JSON data. Please have a look:
var
RESTRequest : TRESTRequest;
RESTClient : TRESTClient;
Response : TRESTResponse;
contract : TJSONObject;
begin
RESTClient := TRESTClient.Create('URL');
try
RESTRequest := TRESTRequest.Create(nil);
try
contract := TJSONObject.Create;
contract.AddPair(TJSONPair.Create('name','my_first_contract.pdf'));
RESTRequest.Client := RESTClient;
RESTRequest.Method := rmPOST;
RESTRequest.Accept := 'application/json';
RESTRequest.AddParameter('j_token','mytoken',pkHTTPHEADER,poDoNotEncode);
RESTRequest.AddBody(contract);
RESTRequest.Execute;
Response := RESTRequest.Response;
ShowMessage(Response.StatusText + ' : ' + Response.Content);
finally
RESTRequest.Free;
end;
finally
RESTClient.Free;
end;
end;
I obtained this error :
Not Found : {"errors":"Fatal error in JsonConvert. Passed parameter json object in JsonConvert.deserializeObject() is not of type object.\n"}
I've read online that the AddBody() method first serializes its content if it's an object. In this case, the content of the body is my TJSONObject, but when I try to replace that with a String, like this:
var
contract : String;
...
begin
contract := '{"name":"my_first_contract.pdf"}';
...
RESTRequest.AddBody(contract, ctAPPLICATION_JSON);
...
end;
I'm getting the exact same error.
So, does that mean that a TJSONObject is not viewed as an Object for the JsonConvert.deserializeObject() method ? Or, is the serialization of the AddBody() messed up?
The problem was on the 'j_token' header : as I was trying to solve it, some friends wanted to help me but I didn't want to give them the access token because it's exclusive to my company. They still tried to access the api with a false token wich resulted with the same error as I was getting :
Not Found : {"errors":"Fatal error in JsonConvert. Passed parameter json object in JsonConvert.deserializeObject() is not of type object.\n"}
Thanks to that I could deduce that the issue was on the j_token. After setting up my own api I could watch what was I posting and then I saw that my 'j_token' header was still getting encoded even though I added the poDoNotEncode options to my AddParameter method.
I created a new post on this forum to look for that poDoNotEncode error if you ever stumble upon this same problem : Trouble with poDoNotEncode option in TRESTRequest.AddParameter() method
I'm trying to upload a Document to an api.
The Api owner sent me his C# Code to upload a Document :
var client = new
RestClient("hisurl");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("v", "4.2");
request.AddHeader("j_token", "histoken");
request.AddFile("file", "/C:/Users/olivm/Documents/QA API Swagger/smartpacte2.pdf","application/.pdf");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
I decided to use the TCustomRESTRequest.AddFile() method as well so here is my code :
procedure AttachDocument(contract_id : Integer; pathFile : String);
var
RESTClient : TRESTClient;
RESTRequest : TCustomRESTRequest;
Response : TCustomRESTResponse;
begin
RESTClient := TRESTClient.Create('');
RESTRequest := TCustomRESTRequest.Create(nil);
try
RESTClient.BaseURL := 'myurl';
RESTRequest.Client := RESTClient;
RESTRequest.Accept := 'application/json';
RESTRequest.Params.AddHeader('j_token','mytoken',[poDoNotEncode]);
RESTRequest.Method := rmPOST;
RESTRequest.AddFile('file',pathFile);
RESTRequest.Execute;
Response := RESTRequest.Response;
ShowMessage(Response.Content);
finally
RESTRequest.Free;
RESTClient.Free;
end;
end;
But this result in a error :
RUNTIME ERROR : Impossible converting variant of type (UnicodeString) to type (Int64)
In fact, it seems like the TCustomRESTRequest.AddFile()method wants my pathFile as the first argument and a Int64 as the second one like this :
RESTRequest.AddFile(pathFile,0);
I also tried this :
RESTRequest.AddFile(pathFile,ctAPPLICATION_PDF);
But each and every time i get this error from the api :
{"errors":"no files found"}
Do you have any idea on how to make this work ?Also, have you ever seen this sort of TCustomRESTRequest.AddFile()method ?
I have been struggling to solve this error for the last 3 days and can't find a way to fix it.
First chance exception at $779ACC12. Exception class EIdSMTPReplyError with message
'Username and Password not accepted.
Before this problem I had a problem with SSL library that couldn't load but fixed it when i got the correct .dll files. Now I'm stuck on this.
The code I used is the following.
var
FormMain: TFormMain;
mail_username: String;
mail_password: String;
mail_to: String;
mail_subject: String;
mail_body: String;
implementation
{$R *.dfm}
procedure Gmail(UserName, Password, ToTarget, Subject, Body : String);
var
DATA : TIdMessage;
SMTP : TIdSMTP;
SSL : TIdSSLIOHandlerSocketOpenSSL;
begin
SMTP := TIdSMTP.Create(nil);
DAta := TIdMessage.Create(nil);
SSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SSL.SSLOptions.Method := sslvTLSv1;
SSL.SSLOptions.Mode := sslmUnassigned;
SSL.SSLOptions.VerifyMode := [];
SSL.SSLOptions.VerifyDepth := 0;
DATA.From.Address := UserName;
DATA.Recipients.EMailAddresses := ToTarget;
DATA.Subject := Subject;
DATA.Body.Text := Body;
SMTP.IOHandler := SSL;
SMTP.Host := 'smtp.gmail.com';
SMTP.Port := 587; // also used the other 2 ports and their respective UseTLS
SMTP.Username := UserName;
SMTP.Password := Password;
SMTP.UseTLS := utUseExplicitTLS;
SMTP.Connect;
SMTP.Send(DATA);
SMTP.Disconnect;
SMTP.Free;
DATA.Free;
SSL.Free;
end;
procedure TFormMain.Button1Click(Sender: TObject);
begin
mail_username := EditUserName.Text;
mail_password := EditPassword.Text;
mail_to := EditTo.Text;
mail_subject := EditSubject.Text;
mail_body := MemoBody.Text;
try
begin
Gmail(mail_username, mail_password, mail_to, mail_subject, mail_body);
end;
except
ShowMessage('Didn''''t send.');
end;
end;
I looked everywhere and couldn't find an answer for this problem. I'm working with Delphi 10.2 Tokyo.
While I was still searching for an answer, I kept finding that people talked about the new security changes Google made. In the end, I enabled 2-Step Verification and made an application-specific password. When I used the app password, the email went through fine.
Here is the way I made the app password:
Sign in with App Passwords
I'm a beginner and trying to retrieve a response from a webservice.
The method (ImportSoap) to send a base64code's pdf and i use a code like this:
procedure TForm1.Button2Click(Sender: TObject);
var
wf:ImportSoap;
res:ArrayOfString;
begin
wf:=(httprio1 as ImportSoap);wf.UploadChunk('*user*','*password*',convertStringToTByteDynArray(Codifica64(Edit1.Text)),res[0],error);
memo1.Lines.Text:=risposta;
end;
The error is:
Server was unable to process request - String reference not set to an instance of a String
Server-side code is:
procedure UploadChunk(const userLogin: WideString; const userPassword: WideString; const data: TByteDynArray; out UploadChunkResult: WideString; out errorDescription: WideString); stdcall;
Can anyone help me?
(sorry for my english)
I must be missing something here!
I have been playing around trying to refresh an expired OAUTH2 token using the new ( new to me anyway, coming from delphi xe2 environment) TOAuth2Authenticator, TRESTClient, TRESTRequest, TRESTResponse components
I have set the following authenticator properties with the existing known values for
ClientID
ClientSecret
Scope
AccessTokenEndPoint
AuthorizationEndPoint
RedirectionEndPoint
AccessToken
AccessTokenExpiry
RefreshToken
and can successful access resources from the REST server, up until the token expires.
I presumed (wrongly, so it seems) if I try an execute a request against a server, and the token had expired, there should be enough detail for the component to realise the token has expired and refresh it as and when it need to.
I take it there is no hidden/undocumented "RefreshExpiredToken" method that I can call?
Any pointers in the right direction would be greatly appreciated :-)
Thanks
I eventually figured this out, by bastardising the publicTOAuth2Authticator.ChangeAuthCodeToAccessToken procedure, but thought I'd post my solution just in case it helps anyone else out:
LClient := TRestClient.Create(AccessTokenURI);
try
LRequest := TRESTRequest.Create(LClient); // The LClient now "owns" the Request and will free it.
LRequest.Method := TRESTRequestMethod.rmPOST;
LSecretBase64 := String(SZFullEncodeBase64(AnsiString(<myClientID>+ ':' + <MyClientSecret>)));
LRequest.AddAuthParameter('grant_type', 'refresh_token', TRESTRequestParameterKind.pkGETorPOST);
LRequest.AddAuthParameter('refresh_token', _AccessRefreshToken, TRESTRequestParameterKind.pkGETorPOST);
LRequest.AddAuthParameter('Authorization','Basic '+LSecretBase64, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode] );
LRequest.Execute;
//Memo1.Lines.Add(LRequest.Response.Content);
if LRequest.Response.GetSimpleValue('access_token', LToken) then
begin
_AccessToken := LToken;
end;
if LRequest.Response.GetSimpleValue('refresh_token', LToken) then
begin
_AccessRefreshToken := LToken;
//Memo1.Lines.Add('RefreshExpiredToken: New Refresh Token Extracted');
end;
// detect token-type. this is important for how using it later
if LRequest.Response.GetSimpleValue('token_type', LToken)
then _TokenType := OAuth2TokenTypeFromString(LToken);
// if provided by the service, the field "expires_in" contains
// the number of seconds an access-token will be valid
if LRequest.Response.GetSimpleValue('expires_in', LToken) then
begin
LIntValue := StrToIntdef(LToken, -1);
if (LIntValue > -1) then
_AccessTokenExpireDT := IncSecond(Now, LIntValue)
else
_AccessTokenExpireDT := 0.0;
//Memo1.Lines.Add('RefreshExpiredToken: New Token Expires '+formatdatetime('hh:nn:ss dd/mm/yyyy', _AccessTokenExpireDT));
end;
finally
LClient.DisposeOf;
end;