PDA

View Full Version : Creating RESTful web services in DataFlex



Mike Peat
31-Oct-2017, 05:51 AM
Hi All

For those with an interest in such things, I have written a white paper on a way to create RESTful JSON web services in DataFlex (19.0+). It uses a class - cRESTfulService - which encapsulates much of the low-level mechanics of doing this and a sample of such a service as well as a Windows client for it.

Available at http://www.unicorninterglobal.com/Company-White-Papers-Creating-RESTful-JSON-Web-Services-in-DataFlex-868.

Any feedback would be welcome. :)

Mike

Ola Eldoy
31-Oct-2017, 06:51 AM
That is brilliant, Mike!

A very useful thing, and fun to read! (I didn't get to the end, but now I know the info is there when I need it.)

Thanks :-)

Mike Peat
31-Oct-2017, 06:52 AM
Glad you liked it! :-)

Hans van de Laar
1-Nov-2017, 03:54 AM
Well done Mike!

Nils G. Svedmyr
1-Nov-2017, 04:00 AM
Thanks Mike. Very well written. If I would have written that even myself would have fallen asleep only getting half way. But you kept me wide awake throughout the whole text :o

Mike Peat
1-Nov-2017, 04:49 AM
:D Thanks Nils and Hans!

Mike Peat
1-Nov-2017, 04:55 AM
Since you are all being so nice about it, I should give credit where it is due. Not only to Harm, who worked out all the clever stuff, but to Marco and John Tuohy who helped with getting it to deal with special language characters better (as did Harm) and to Klaus, who went through it and found the mistakes (now hopefully fixed).

Mike

Mike Cooper
2-Nov-2017, 05:18 AM
Great paper Mike. Lots of excellent information and screen shots. Well done!

Mike Peat
2-Nov-2017, 06:26 AM
Thank you! :o

wila
2-Nov-2017, 05:21 PM
Great stuff Mike.

Added your site to the vdf-guidance resources page links, not sure why it wasn't on that list yet as I've read some of your articles on there before and learned from it.
Always an enjoyable read.

Thanks!
--
Wil

Mike Peat
3-Nov-2017, 02:59 AM
Thanks Wil. :o

danwalsh46
3-Nov-2017, 05:13 AM
+1

Richard Hogg
3-Nov-2017, 06:38 PM
Great stuff Mike. Thanks for the effort it's much appreciated.

Cheers
Richard

danwalsh46
4-Nov-2017, 01:35 PM
Mike,

This is great stuff, brilliant. I worked through it this morning from beginning to end on my local machine. Very well documented and easy to follow.

Of the two links to download URL Rewrite, I'm guessing the first is the one to use on a 2012 server. Can anyone confirm? I want to put something on my AWS server.

Mike Peat
5-Nov-2017, 06:38 AM
Dan

I think the best way to do it is just to get onto the server and search for "URL Rewrite", then follow the links to an official Microsoft site. The links seem to change from time to time. The pages will probably work out what platform you are on and take you to the right download.

Mike

chuckatkinson
5-Nov-2017, 07:25 AM
Well done Mike. I read most of it. Very clear.

I'm thinking presentation at DISD. Any chance you are coming or want to do a remote again?

danwalsh46
5-Nov-2017, 07:31 AM
Using the server's Search didn't get me anywhere, but a Google search specifying IIS 8.0 takes me to the download you recommended for Windows 10. It installed and opens okay. I'll report back here if there is any issue when I try to use it.

Mike Peat
5-Nov-2017, 08:00 AM
Chuck

I can realistically only come if somebody pays my way, but I am always happy to do a remote.

Mike

chuckatkinson
6-Nov-2017, 09:44 AM
Thanks, remote is the only realistic possibility since DISD has basically no budget. Maybe we could start a GoFundMe for you? :D

Mike Peat
6-Nov-2017, 11:06 AM
:rolleyes:

Nathaniel
13-Nov-2017, 11:12 AM
Thanks Mike, I appreciate your writing style!

Your article was fun to read (from top to bottom) and helpful as I will no doubt be working to implement RESTful APIs using your article as an important reference point.
nb

Mike Peat
14-Nov-2017, 05:00 AM
Glad you liked it! :)

Mike

Sam Neuner
14-Nov-2017, 03:35 PM
Thanks for the contribution:). Your documentation was very easy to follow!!

I am having a problem getting the sample to work when changing to my localhost. Your website works fine. However, when I try to run it from my localhost I am getting a 404 error. My webservice ("ContactsREST") is running. I changed the C_Call_Path to "ContactsREST/REST/". Any ideas?

danwalsh46
14-Nov-2017, 04:11 PM
Sam,

I want to remember this happening to me after I got ahead of myself by copying in Mike's sample and not dotting all the i's. I'd check the Project Properties and make sure the WebApp is property registered.

Sam Neuner
14-Nov-2017, 04:57 PM
I noticed in IIS on my "RewriteREST" that the "Input" says "URL Path" not as in the documentation example where is has "URL path after 'Contacts'". I cannot find a way to change the Input URL Path.

danwalsh46
14-Nov-2017, 05:22 PM
If you select the rule, you will see a Remove option to the right. Maybe you should remove and try it again.

Sam Neuner
14-Nov-2017, 05:37 PM
Did this! There was no place that I saw to change this variable.

Mike Peat
15-Nov-2017, 02:54 AM
Sam

Are you sure you are creating the rule in the app's virtual directory? It sounds like you are creating it in the root of the web site.

Mike

Sam Neuner
15-Nov-2017, 07:49 AM
YES! That is the one piece of the doc I did not pay enough attention to! I will give that a try.

Thanks again for this contribution you have made. How did you figure out what all needed to be done in IIS?

Sam Neuner
15-Nov-2017, 09:54 AM
Works like a charm!!! Thanks so much:o.

Mike Peat
15-Nov-2017, 02:28 PM
:)

Sam Neuner
15-Nov-2017, 04:38 PM
Can there be as many "parameters" as needed in the call to the web service? Is assume each one is separated by a "/"? Also, can there be spaces in the "parameters"?

Marco
15-Nov-2017, 04:44 PM
Hi Sam
Just like in any url, spaces would need to be encoded as %20, like embedded ‘/‘ and any other character that needs encoding.

Sam Neuner
15-Nov-2017, 04:57 PM
I just remembered (if correctly) that "parameters" (data) are passed as an array/struct in the HttpVerbAddrRequest, so spaces would not be an issue?

Sam Neuner
15-Nov-2017, 05:12 PM
I guess I was really talking about the "sFilePath" parameter in the "Function HttpVerbAddrRequest String sFilePath Address aData Integer iDataLen Integer bDataIsFile String sVerb Returns Integer". As in the example Mike has in the "Contacts" REST, the "Email" address is appended to "Contact/" and passed as the sFilePath (along with the C_callPath). For example, if I wanted to pass a name in addition to the email address and the name has a space(s) in it ("Contact/" + sEmail + "/" + sName + "/" dBeginDate + "/" dEndDate).

Sam Neuner
15-Nov-2017, 05:43 PM
I tested this in IIS "Test" area and it looks like spaces are OK?

Sam Neuner
17-Nov-2017, 12:55 PM
Still playing around with your RESTful API. In testing one I have made, I am building a grid as you did in your example. Everything is working except I am getting an error when building the grid. This grid still gets built properly and the data is displayed. It is in this code just as you have in your example:



Move (MakeCall(hoHttp, "GET", "StudentList" + sQuery, "", False)) to hoJson
Send Destroy of hoHttp

If hoJson Begin
Get JsonToDataType of hoJson to tErr *** This generates and error: "Invalid JSON Object: Expected JSON Object" - but it continues on and displays my grid because tErr.Code = 0

If (tErr.code = 0) Begin
Get JsonToDataType of hoJson to tStudents
Send Destroy of hoJson


Is it because the tErr is not the same structure as tStudents. However, this does not generate an error in your sample. Any ideas?

Mike Peat
18-Nov-2017, 06:52 AM
Sam

It is a non-obvious (almost hidden) rule of API design that your methods should return JSON objects, not JSON arrays, for exactly this reason. The mutability of JSON is such that you can return an error object instead of what the client was expecting (to inform them of some problem), but not if they were expecting an array.

I am guessing that this might be the case here. If so, return an object with an array of students rather than just an array of students.

Mike

danwalsh46
18-Nov-2017, 10:09 AM
Thanks for this little pearl. It clarifies/explains the following struct:


Struct tContactList
tShortContact[] contacts
End_Struct

Sam Neuner
18-Nov-2017, 12:33 PM
So, after viewing Dan's response, an Array struct inside a Struct is considered an object? (i.e. your tContactList - tShortContact[] contacts)

Mike Peat
19-Nov-2017, 08:29 AM
Sam

The key is the outer object. You can get an array of JSON objects: [{"x":"y"}, {"x": "z"}, ... ] into a cJsonObject, but you can't then get that out into a struct, only an array of structs.

Since it is handy to be able to check for the return of an error object, we always need to return objects, even if that outer object then contains an array of other objects. You can return arrays, but then you will get an error if you check for an error-condition by seeing if you got an error object back.

The array inside the struct is an array (of objects, most commonly), not an object, but what the cJsonObject contains is an object (the outer struct wrapper for the array), which then causes no problem when you try to get it out via "Get JsonToDataType" to an error-object struct. If it contains an array (of whatever), you can only get it out into an array data-type: say tStudents[]; not tErr, which is not an array, but a struct.

Clearer?

Mike

Sam Neuner
19-Nov-2017, 11:28 AM
Yes, thanks. Still very much a novice at the JSON stuff!!! I have my WebService working well thanks to your code and examples. Thanks again.

Mike Peat
20-Nov-2017, 04:08 AM
Hth! :)

Mike Peat
1-Dec-2017, 08:31 AM
Sam

Sorry, this slipped by me.

Yes, you can go as deep as you like. It just parses the string that comes from the ORIGINAL_REQUEST server variable splitting it into an array property on "/". I don't think it ought to have spaces in it because that is forbidden by the rules for URLs, but I suspect my code wouldn't care. The usual route if you require spaces for some reason is to URL-encode them as %20 (many other such replacements are available (https://www.w3schools.com/tags/ref_urlencode.asp)).

Mike

Sam Neuner
1-Dec-2017, 09:23 AM
Thanks.

Clive Richmond
29-Jan-2019, 12:51 AM
Hi Mike,


For those with an interest in such things, I have written a white paper on a way to create RESTful JSON web services in DataFlex (19.0+). It uses a class - cRESTfulService - which encapsulates much of the low-level mechanics of doing this and a sample of such a service as well as a Windows client for it.

Excellent piece of work Mike and thanks for sharing.

One of the bugbears I have with SOAP is its inability to determine if an element was set with a value or to be ignored i.e. NULL. (https://support.dataaccess.com/Forums/showthread.php?61905-Webservices-Structs-with-empty-values) Your RESTful service and JSON offers some light.

In your method UpdateContact I have modified the code to check if the member exists in the json object passed. The assumption being, if it is then the column should be updated which allows it to be cleared.

Anyway, thought you and others might find this useful (especially when it comes to dealing with integers and numbers).


Get phoJsonData To hoJsonData
Get JsonToDataType Of hoJsonData To tCont

If (HasMember(hoJsonData, "email")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.EMail To tCont.email
End
If (HasMember(hoJsonData, "name")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.Name To tCont.name
End
If (HasMember(hoJsonData, "Address")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.Address To tCont.Address
End
If (HasMember(hoJsonData, "city")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.City To tCont.city
End
If (HasMember(hoJsonData, "postcode")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.Postcode To tCont.postcode
End
If (HasMember(hoJsonData, "telHome")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.TelHome To tCont.telHome
End
If (HasMember(hoJsonData, "telMobile")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.TelMobile To tCont.telMobile
End
If (HasMember(hoJsonData, "telWork")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.TelWork To tCont.telWork
End
If (HasMember(hoJsonData, "birthday")) Begin
Set Field_Changed_Value Of oContacts_DD Field Contacts.Birthday To tCont.birthday
End

Mike Peat
29-Jan-2019, 03:21 AM
Clive

Thank you, but that one is a long way out of date now.

On one hand DF 19.1 gets rid all the need for messing around with ASP and URL Rewrite (although knowing how to work with the latter is still important), while on the other I have moved on a very long way with a set of REST classes which change the approach to how to deal with the JSON. Will we be seeing you at Synergy? I'm training and presenting on the topic there.

Mike

Clive Richmond
30-Jan-2019, 11:25 AM
Hi Mike,


On one hand DF 19.1 gets rid all the need for messing around with ASP and URL Rewrite (although knowing how to work with the latter is still important), while on the other I have moved on a very long way with a set of REST classes which change the approach to how to deal with the JSON. Will we be seeing you at Synergy? I'm training and presenting on the topic there.

I had wondered if you had taken advantage of the changes made to the cWebBusinessProcess class in 19.1. You have obviously gone much further and your RESTful JSON web services has come on in leaps and bounds. Very much looking forward to the next chapter.

Sadly, we won’t have a representative from Triumph attending this year’s Synergy.

Mike Peat
30-Jan-2019, 11:27 AM
:(

Edgar H. Peña C.
21-Jul-2019, 11:18 AM
Hi

I have to vent.
It seems unbelievable that DAC does not offer us a robust development to consume API, a resource that has been very used in recent years. When we need to consume an API, everything works in POSMAN and SOAPUI but we crash with DF. The little he has is a puzzle for the poor documentation. I have a subscription for the last 10 years, but what is the use if we have to buy additional products like chilkat ?.

Edgar

Mike Peat
21-Jul-2019, 01:07 PM
Edgar

You don't need to buy additional products, you can do it all in DataFlex, which is what this sub-forum is all about.

The cHttpTransfer (https://docs.dataaccess.com/dataflexhelp/index.htm#t=mergedProjects%2FVDFClassRef%2FcHttpTr ansfer.htm) class, or the cJsonHttpTransfer (https://docs.dataaccess.com/dataflexhelp/index.htm#t=mergedProjects%2FVDFClassRef%2FcJsonHt tpTransfer.htm) class, will do what is required for actually consuming RESTful APIs.

For dealing with authentication, there are other classes here that will help with that as well. There are copious examples for using OAuth 2.0, for instance. For Basic Authentication ("Basic Auth"), you only need to set the psUserName (https://docs.dataaccess.com/dataflexhelp/index.htm#t=mergedProjects%2FVDFClassRef%2FInetTra nsfer-Property-psUserName.htm) and psPassword (https://docs.dataaccess.com/dataflexhelp/index.htm#t=mergedProjects%2FVDFClassRef%2FInetTra nsfer-Property-psPassword.htm) properties of the two classes above.

Mike

Edgar H. Peña C.
21-Jul-2019, 02:00 PM
Mike:

I'm lost in this link

https://support.dataaccess.com/Forums/showthread.php?64655-cJsonHttpTransfer-pkg

Edgar

Edgar H. Peña C.
21-Jul-2019, 02:14 PM
Mike

This works well in SOAPUI (The token I send it to the email, I add it to the header)

Edgar

danwalsh46
22-Jul-2019, 08:06 AM
Edgar,

See my Using the MailChimp API presentation for some example code. You will find it here:

http://www.disdconference.com/index.php/archives/2018-presentations/

Edgar H. Peña C.
23-Jul-2019, 05:03 AM
thanks Dan