PDA

View Full Version : Using RestAPI with HTTPostRequest in DataFlex 17.1



MrHalland
25-Nov-2013, 11:21 AM
Hi!

I have a request from a customer to implement some functions in VDF 17.1.

In their end they have some sort of RestAPI. All the samples they sent me uses this software called CURL.

One of the post operations does not return anything else but a simple ok result (Message 200 from the web).
If issued in dos, it sets a cookie. If I do a HTTPPostRequest in VDF I would like to get the created cookie, but I just don't know how.

An example of sending a login and getting a response back in form of a cookie (I guess it is used for a sessionticket) using the CURL



C:\Curl\curl -v -X POST -d "domain_id=texi&username=myname&password=mypassword" http://193.xxx.xxx.xxx:41900/Login


The response from the server is:



* About to connect() to 193.xxx.xxx.xxx port 41900 (#0)
* Trying 193.xxx.xxx.xxx... connected
* Connected to 193.xxx.xxx.xxx (193.xxx.xxx.xxx) port 41900 (#0)
> POST /login HTTP/1.1
> User-Agent: curl/7.16.4 (i386-apple-darwin9.0) libcurl/7.16.4 OpenSSL/0.9.7l zlib/1.2.3
> Host: 193.xxx.xxx.xxx:41900
> Accept: */*
> Content-Length: 48
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< Set-Cookie: JSESSIONID=uw90urfx2j6c1samv49q4l3ae;Path=/
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Content-Length: 0
< Server: Jetty(8.0.4.v20111024)
<
* Connection #0 to host 193.xxx.xxx.xxx left intact
* Closing connection #0



So what I want to do is to use the HTTPPostRequest in VDF 17.1 if possible.

How to get the JSESSIONID cookie that's returned?

This world, with RestAPI, JSON is totally new to me.

Any help, point to the right direction etc. is greatly appreciated.

Regards
Martin

Peter van Mil
25-Nov-2013, 05:05 PM
Hi Martin,

I have connected to a REST webservice and I am using a post of loginname and password to get a token.

To test this REST webservice I use the Postman addin of Google Chrome. This helped me to communcate with the owner of the REST webservice. If you say that you are using Visual DataFlex, it will be Always your fault if it doesn't work :(.

Todd Forsberg
2-Dec-2013, 09:41 AM
Peter, could you show an example of how you do the post of loginname and password? I'm having trouble getting past the authentication.

Todd Forsberg
2-Dec-2013, 09:51 PM
Have you tried using Https (for secure RESTful services) ?

Peter van Mil
3-Dec-2013, 02:22 AM
Hi Todd,

The big trick of this specific REST service is getting the "x-wsse" header. This header is needed with all other function calls. See code. I don't know how other REST services work.



// 201 Returned when the token is successfully created
// 403 Returned when the supplied credentials are invalid

// Parameters: username, password

Function PkiLogin Returns tPkiLogin
String sFilePath sData sWsseCode
Integer iRetVal iResponse

tPkiLogin Login
Set psReturn to ""

Move "/rest/token/new.json" to sFilePath
Move ("username=" + csPkiUserName + "&password=" + csPkiPassword) to sData
Get AddHeader "CONTENT-TYPE" "application/x-www-form-urlencoded" to iRetVal
Get HttpPostRequest sFilePath sData False to iRetVal

Get ResponseStatusCode to iResponse

If (iResponse = 201) Begin
Get ResponseHeader "x-wsse" 0 to sWsseCode
End
Else Begin
Move "" to sWsseCode
End

Move (iResponse = 201) to Login.bLoggedIn
Move iResponse to Login.iResponse
Move sWsseCode to Login.sWsseCode

Function_Return Login
End_Function

//////////////////////////////////////////////////////

Function PkiGetTheaters String sWsseCode Returns tPkiTheaters


/////////////////////

Move "/rest/theaters.xml" to sFilePath

Get AddHeader "CONTENT-TYPE" "application/json" to iRetVal
Get AddHeader "x-wsse" sWsseCode to iRetVal
Get HttpGetRequest sFilePath to iRetVal

etc.

Peter van Mil
7-May-2014, 06:01 AM
Hi Todd,

I didn't use it before, but I am struggling with it now. I was expecting, that setting the property piRemotePort to rpHttpSSL (=443) is sufficient. For some reason it doesn't work.

Did you get any further?

MrHalland
7-May-2014, 08:11 AM
Hi!

I got it to work, but i dont send it over Https. But if there is a Https, there should be a certificate involved, is it up to date on the remote computer, or do you need to install a certificate on your computer?

There is a property for setting messages to see if a certificate is needed, right now i don't remember what the property is. I need to get back.

Peter van Mil
7-May-2014, 08:15 AM
After searching this forum I have found it.

You have to set these two properties together:



Set piRemotePort to rpHttpSSL
Set peTransferFlags to ifSecure

eriksven
7-May-2014, 01:42 PM
I use this class for consuming RESTful webservices. Maybe it will do the trick for you?



https://github.com/eriksven/VdfWebTools/blob/master/cJSONWebClient.pkg.


It handles http/https protocols, paths, encodings and querystrings for you. It's a subclass of the cWebClient, which is documented here: http://eriksven.com/visual-dataflex/web-client/

Example requests:


Handle hClient
String sJsonResponse sResponseHeader

Get Create U_cJSONWebClient to hClient

// Set request headers
Set RequestHeader of hClient to "a-header-name" "a-header-value"

// GET Request
Get DownloadJSONString of hClient "https://some-webservice.com/records?id=234" to sJsonResponse

// POST Request
Get UploadJSONString of hClient "https://some-webservice.com/update" '{"id":234, "name":"test"}' to sJsonResponse

// PUT Request
Get PutJSONString of hClient "https://some-webservice.com/create" '{"id":234, "name":"test"}' to sJsonResponse

// Retrieve response headers
Get ResponseHeader of hClient "a-header-name" to sResponseHeader

Send Destroy of hClient

Peter van Mil
7-May-2014, 03:14 PM
Hi Erik,

If I had to start today with a new project, I would choose for this. At this moment I have too much with XML to start all over. (Maybe will do it later anyway).

Ezequiel Marin
23-Jun-2014, 08:48 AM
HI Erik, thank you for share your code.


I am testing cJSONWebClient.pkg and have sucess until now, but result of json is bigger than maximum length of string variables can support.


Do you some tip do solve this ?




Thank you again


Regards


Ezequiel Marin
São Paulo / Brasil





I use this class for consuming RESTful webservices. Maybe it will do the trick for you?



https://github.com/eriksven/VdfWebTools/blob/master/cJSONWebClient.pkg.


It handles http/https protocols, paths, encodings and querystrings for you. It's a subclass of the cWebClient, which is documented here: http://eriksven.com/visual-dataflex/web-client/

Example requests:


Handle hClient
String sJsonResponse sResponseHeader

Get Create U_cJSONWebClient to hClient

// Set request headers
Set RequestHeader of hClient to "a-header-name" "a-header-value"

// GET Request
Get DownloadJSONString of hClient "https://some-webservice.com/records?id=234" to sJsonResponse

// POST Request
Get UploadJSONString of hClient "https://some-webservice.com/update" '{"id":234, "name":"test"}' to sJsonResponse

// PUT Request
Get PutJSONString of hClient "https://some-webservice.com/create" '{"id":234, "name":"test"}' to sJsonResponse

// Retrieve response headers
Get ResponseHeader of hClient "a-header-name" to sResponseHeader

Send Destroy of hClient

Focus
23-Jun-2014, 08:52 AM
set_argument_size ?

Nicholas Herlick
8-Dec-2017, 05:04 PM
Erik,

We are using your parsing object to parse a single jsonrecord, but the next project must parse an array of json records.

Example of current use...
Handle hParser hDictionary
Get Create U_cJSONParser to hParser
Get Create U_cJSONDictionary to hDictionary
Send Parse of hParser sJson hDictionary
Get Value of hDictionary "first_name" to ResponseData.sFirstName
Get Value of hDictionary "status" to ResponseData.sStatus

how would that be done if sJason contains five records?
Handle hParser hDictionary
Get Create U_cJSONParser to hParser
Get Create U_cJSONDictionary to hDictionary
Send Parse of hParser sJson hDictionary
Get Value of hDictionary "first_name" to ResponseData[iItem#].sFirstName
Get Value of hDictionary "status" to ResponseData[iItem#].sStatus

We know how to load up a struct array, but not how the parser handles sJson consisting of many records. Thanks for any guidance.

Nick

Mike Peat
11-Dec-2017, 03:42 AM
Hi Guys

I do this all the time. In addition to setting piRemotePort of the cHTTPTransfer object to rpHttpSSL (actually 443) I also set peTransferFlags to ifSecure.

I would advise standardising on using the new HttpVerbAddrRequest rather than using different commands (HttpPostRequest, HttpGetRequest, etc.) for different request types - it just makes your code more consistent and allows you to handle stuff in one place, however I don't think that became available until ~18.1.

You can set the psUsername and psPassword properties of the HttpTransfer object to fulfill that requirement.

Using HTTPS does not usually require a certificate at your end - that is required on the server offering the HTTPS endpoint.

Take a look at some of the code in my OAuth2 or MSOffice365 samples (in the OAuth, RESTful & Web API Integration sub-forum (https://support.dataaccess.com/Forums/forumdisplay.php?59-OAuth-RESTFul-amp-Web-API-Integration)) to see how to do this stuff, however please note that the JSON handling code in those is out of date as of 19.0, which introduced JSON objects to the product.

For at least two reasons above you should ideally be moving out of 17.1 and onto 19.0+ for this stuff - the tools are just better!

If you have other questions, just ask (but best in that forum).

Mike (Dammit - this went to the wrong place in the thread! :()

Mike Peat
11-Dec-2017, 04:04 AM
Guys

Before we got JSON objects in 19.0, I used to do the following (I'm just typing this in from memory, not copying it from working code, so there will be typos!):



Use cHttpTransfer

Object oHttp is a cHttpTransfer
Property UChar[] pucaData

Procedure OnDataReceived String sContentType String sData
UChar[] ucaData

Get pucaData to ucaData
Move (AppendArray(ucaData, StringToUCharArray(sData))) to ucaData
Set pucaData to ucaData
End_Procedure

Procedure Reset
UChar[] empty

Set pucaData to empty
End_Procedure

End_Object

// elsewhere

Send Reset of oHttp

// Set up other stuff and make HTTP call of oHttp to bOK

If bOK Begin
Get ResponseStatusCode of oHttp to iStat

If ((iStat >= 200) and (iStat < 300)) Begin
Get pucaData to ucaResp

Get_Argument_Size to iOldArgSize

If (SizeOfArray(ucaData) > iOldArgSize) Begin
Set_Argument_Size (SizeOfArray(ucaData) + 1024) // Just being cautious! ;-)
End

Move (UCharArrayToString(ucaData)) to sResponse

// Do what needs to be done with the response

Set_Argument_Size iOldArgSize
End

// Errors handling code for not bOK and duff HTTP status codes



Which is a bit ugly, but lacking JSON objects was the best I could come up with.

(Note: Get_Argument_Size has a "to"; Set_Argument_Size does not.)

Mike

Jeff Kimmel
13-Jan-2018, 01:40 PM
Nick,

Not sure if you've figured this out yet but this is how I do it. I get the 'ValueAtPath' to a variant array. From there I parse the fields as necessary (code posted below).

Hope this help,

Jeff

Variant[] myList
Integer i iCount
Get ValueAtPath of hDictionary "myList" to myListMove (SizeOfArray(buList) -1) to iCount
For i from 0 to iCount
Get ValueAtPath of myList[i] "field1" to var1
Get ValueAtPath of myList[i] "field2" to var2
Get ValueAtPath of myList[i] "field3" to var3
Loop

Lennart_T
22-May-2019, 02:42 AM
Hello Erik,

I tried to use your code for consuming a RESTful webservice. The secong time I send the same Get request, the HTTPGetRequest returns 1 WITHOUT SENDING THE REQUEST.

My code calls Get Create U_cJSONWebClient to hClient only once and keeps the handle in a property. Do I have to call it for each request? (with a destroy afterwards.) EDIT: This did not help. I have to exit the program and start it again. :(


Function _DoGetRequest String sURL Handle hTransfer Returns Boolean
_tcWebClientUri Uri
Integer iStatus
Send _ClearDataReceived of (phTransfer(Self))
Get _URISegments sURL to Uri
Send _PrepareRequest Uri hTransfer
Get HTTPGetRequest of hTransfer (Uri.Path + Uri.Name + If(Uri.Query <> "", "?" + Uri.Query, "") + If(Uri.Hash <> "", "#" + Uri.Hash, "")) to iStatus // This line does not send the same request twice.

Function_Return (iStatus <> 0)
End_Function


/Lennart Thelander

Lennart_T
22-May-2019, 04:03 AM
I found this thread about the same problem I have:
https://support.dataaccess.com/Forums/showthread.php?63174-httpgetrequest&highlight=HTTPGetRequest

The solution seems to be sending a bogus header like ?TimeSent2019-08-08-0655 and change it every time.

There has to be a better way.
Anybody?