I'm working on a DElphi FMX project where I make a lot of requests to a REST backend.
I'm using one TRESTClient, and are dynamically creting a TRESTRequest every time I ask for something, my plan is to make it multitreading to make the UI be more responsive.
I have made a function called DoRequest, that do the actual request, it looks like something this:
Function TFormMain.Dorequest(Meth:TRESTRequestmethod;Url,RequestBody:String;
Var Respons: TCustomRESTResponse):Boolean;
Var
par: TRESTRequestParameter;
Req : TRESTRequest;
begin
Req := TRESTRequest.Create(RESTClient);
With Req do
try
if RequestBody<>'' then
begin
Params.Clear;
par := Params.AddItem('body',RequestBody);
par.ContentType := ctAPPLICATION_JSON;
par.Kind := pkREQUESTBODY;
end;
Method := Meth;
Resource := Url;
Execute;
Respons := Response;
Result := Respons.StatusCode In [200,201];
finally
Req.Free; // <-Error here
end;
end;
I call it like this:
procedure TFormUser.Button1Click(Sender: TObject);
Var
Respons: TCustomRESTResponse;
begin
If DoRequest(rmPOST,CreateUserUrl,JSON,Respons) then
begin
ShowMessage('User '+EdEmail.Text+' created');
ModalResult := MrOk;
end
However, I get an Acces Violation when freeing Req, my TRESTRequest.
I found out that it is because the TCustomRESTResponse Respons is just made a pointer to the memory where the Req.Response is placed, so freeing Req also destroys Respons.
My question is: How do I make a copy of Req.Response that lives after Req is freed?
I have tried Respons.Assign(Response), but it gives the rather strange error: "Cannot assign a TCustomRESTResponse to a TFormMain"
Am I doing this the wrong way, also keeping in mind that I would like the actual request to be in another tread?
Michael
When you don't link your own TRESTResponse to the client, the client will create one of its own and also free it. If you create your own TRESTResponse and assign it, the client will leave it alone, and you can free it whenever you want.
That said, like I mentioned in the comment, I think it's probably better to let the thread deal with deserializing the response, and have it pass a more generic class or record holding the relevant information (a value object, if you're into that), rather than passing around the specific TRESTResponse.
Related
I am trying to update a Sharepoint file's metadata using the Indy TidHTTP component in my Delphi program.
Within this program I have successfully managed to do everything else I need to do with the Sharepoint files (create/rename/delete/edit/Checkout etc). But whenever I try to update some metadata property I get a "400 Bad Request" exception.
I know that my RequestDigest string is correct because I am able to upload new contents to the file.
I have seen many similar problems reported and have tried all of the suggestions without success.
(One suggestion was to access the file as a list item rather than using the file url, which I have done in the code below.)
var
str,body,MyUrl: string;
stream: TStringStream;
begin
MyUrl:=MySite+'_api/web/Lists/getByTitle(''CM Library'')/Items(2)';
body:='{"__metadata":{"type":"SP.Data.CM_x0020_LibraryItem"},"Title":"UpdatedTitle"}';
stream := TStringStream.Create(body, TEncoding.UTF8);
try
IdHTTP.Request.Accept:='application/json;odata=verbose';
IdHTTP.Request.ContentType:='application/json';
IdHTTP.Request.ContentLength:=stream.size;
IdHTTP.Request.CustomHeaders.Values['X-RequestDigest']:=RequestDigest;
IdHTTP.Request.CustomHeaders.Values['IF-MATCH']:='*';
IdHTTP.Request.CustomHeaders.Values['X-HTTP-Method']:='MERGE';
try
str := IdHTTP.Post(MyUrl,stream);
except
on E : Exception do
ShowMessage('Exception: '+E.Message);
end;
finally
stream.Free;
end;
// Displays 'Exception: HTTP/1.1 400 Bad Request'
I found it! I needed to add ";odata=verbose" to Request.ContentType (as well as Request.Accept)
Sorry to trouble the community, but I was struggling with this for several days!!
I have a REST-Client Standalone Desktop-Application in which I can enter a URL and can choose what kind of REST Method I want to execute (POST, GET, PUT etc.)
the client primarily serves the function to send a Body-Message (XML-Format). inside the message all the necessary information to handle the message is contained.
the client also has a Basic authenticator so each Request is also sending a Username and password.
With the help of the Delphi XE8 Application Wizard I created a simple Standalone Firemonkey REST-Server with Authentication and Authorization.
When I send a Request from the Client-Application then my Server is able to recieve that Request inside the "WebModuleDefaultAction"-function which has been added by the Wizard.
What I would like to know is wether or not this is the proper place to check the Authorization of the Request for legitimicy.
It would look like this:
procedure TWebModule1.WebModuleDefaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
s:string;
Username:string;
Password:String;
begin
s:=Copy(Request.Authorization,7,length(Request.Authorization)-6);
s:=TNetEncoding.Base64.Decode(s);
if (Username='JohnDoe') and (Password='MyPassword') then
begin
if (Request.InternalPathInfo='') or (Request.InternalPathInfo='/') then Response.Content:=ReverseString.Content
else if (Request.InternalPathInfo='ShowContent') or (Request.InternalPathInfo='/ShowContent') then Response.Content:=PP_ShowContent.Content
else Response.SendRedirect(Request.InternalScriptName + '/');
handled:=true;
end
else
begin
Response.Content:='Unauthorized';
handled:=false;
end;
end;
The user-check is static just for now, as the authorization is meant to be presented as a proof of concept.
However, if I do Authorization like this then I expect this function will grow way too big someday and I also miss out on ServerRoles. Unfortunately I am not versed enough in the topic of REST development to know what the proper way to do anything is.
I have seen that there is also an Authentication Manager that has been added by the Wizard. But as before I don't know what to do with it.
Try to use the component TDSAuthenticationManager connected with your TDSServer.
The component TDSAuthenticationManager has a event called AuthenticationManager that is called before create user session. If your variable "valid" is true in the end, the user will authenticate.
See this https://edn.embarcadero.com/br/article/41267
and this https://www.embarcadero.com/images/dm/technical-papers/rest-servers-in-delphi-xe-using-datasnap.pdf
I'm using huandu/facebook for Golang to access the FB API. https://github.com/huandu/facebook
This works really well locally but when I try to run from the Google App Engine environment, I can't get it to run.
I used this code locally:
res, err := fb.Get("/me", fb.Params{
"fields": "id,first_name,last_name,name",
"access_token": usertoken,
})
In the documentation (link above) they do mention the App Engine environment but I can'f figure out how to ge this to work with the fb.Get convention.
Thanks.
Edit
Almost got it to work!:
// create a global App var to hold app id and secret.
var globalApp = fb.New("<appId>", "<appSecret>")
session := globalApp.Session(usertoken) //User token here
context := appengine.NewContext(r) //Not sure what r should be...
session.HttpClient = urlfetch.Client(context)
res, err := session.Get("/me", nil)
if err := json.NewEncoder(w).Encode(res); err != nil {
panic(err)
}
If I do this I get back the Id and name. Now all I need to do is request the other parameters. Do I do this in the r parameter to the app engine context?
To answer the last question asked, the appengine.NewContext(r) function takes a *http.Request as a parameter, but this refers to the current request, the one your code is executing in. You can use r.URL.Query() if you wanted to get the query parameters that were sent to this request.
If you want to send parameters in another request, like the one to the Facebook API, you can include them directly in the URL you pass to session.Get(). You can use url.Values.Encode if you want to build a query string from a map of values. If you need to make a request using a method other than GET, such as to an API method that expects JSON, you can use http.NewRequest eg.
session.HttpClient = urlfetch.Client(context)
request, err := http.NewRequest("PUT", url, strings.NewReader("{ "someProperty": 1234 }"))
response, err := session.Do(request)
Well, I'm developing a REST Client application that needs to send a POST request using application/x-www-form-urlencoded as Content type. I'm using Delphi's default REST.Client components. I need to send data in XML format, like the following example:
data=<serviceLocal>
<description>Francisco Hansen</description>
<active>true</active>
<corporateName>Francisco Hansen</corporateName>
<country>Brasil</country>
<state>PR</state>
<city>Pato Branco</city>
<cityNeighborhood>Centro</cityNeighborhood>
<streetType>Rua</streetType>
<street>Tocantins</street>
<streetNumber>2334</streetNumber>
<streetComplement>Ap101</streetComplement>
<zipCode>85501-272</zipCode>
<cellphoneStd>46</cellphoneStd>
<cellphoneNumber>99999999</cellphoneNumber>
<phoneStd>46</phoneStd>
<phoneNumber>99999999</phoneNumber>
</serviceLocal>
How can I add all these as POST parameters to a TRestRequest and then send this request using a TRestClient?
Using a Rest.Client.TRESTRequest component, I've been able to set the POST parameters for the request. I've named the TRESTRequest as rqst1
.
rqst1.Method := rmPost;
rqst1.Params.AddItem; //Adds a new Parameter Item
rqst1.Params.Items[0].name := 'data'; //sets the name of the parameter. In this case, since i need to use 'data=' on the request, the parameter name is data.
rqst1.Params.Items[0].Value := params; //Adds the value of the parameter, in this case, the XML data.
rqst1.Params.Items[0].ContentType := ctAPPLICATION_X_WWW_FORM_URLENCODED; //sets the content type.
rqst1.Params.Items[0].Kind := pkGETorPOST; //sets the kind of request that will be executed.
I am trying to use Delphi XE8 RESTClient,RESTRequest and RESTResponse to optain the content of the response to the following API call:
https://api.hubapi.com/contacts/v1/lists/all/contacts/all?hapikey=Demo&count=1000
The call runs fine in C# and in a webbrowser.
I'm not sure how to configure RESTClient and RESTRequest properties.
Can anyone breakdown the API call into RESTClient & RESTRequest properties for me?
I've been wrestling with this for hours and no success so far.
Below is a simple example of the assignment of properties for the REST components.
Creating a new project and dropping a Button, RESTClient, RESTRequest, RESTResponse, and Memo on the form - you can use the code below on your Button Click event to see it work.
procedure TForm1.Button1Click(Sender: TObject);
begin
RESTRequest1.Client := RESTClient1;
RESTRequest1.Response := RESTResponse1;
RESTClient1.BaseURL := 'https://api.hubapi.com';
RESTRequest1.Resource :=
'contacts/v1/lists/all/contacts/all?hapikey=Demo&count=1000';
RESTRequest1.Execute;
Memo1.Text := RESTResponse1.Content;
end;