How to use tsNetPost to perform a HTTP POST with callbacks

Using callbacks with tsNet

The tsNet external supports using callbacks for all transfer types.  Using callbacks allows your scripts to continue executing without waiting for the transaction to complete.

Once the transfer has completed, the callback handler will be called with a number of parameters that allows the user to identify the connection that has completed.

An example using HTTP POST

To initiate a HTTP POST request in a non-blocking manner, the tsNetPost function can be used.  The syntax for this function is:

tsNetPost(pConnectionID, pURL, pHeaders, pPostData, pCallback, [pSettings])

Let us work through an example script that uses this function call so that we can see how to use callbacks with tsNet.

In this example, we will perform the requests inside the mouseUp handler, so here we go!

on mouseUp
   local tUrl, tPostData, tHeaders, tError
   
   put "https://downloads.techstrategies.com.au/tsnet/getentry.php" into tUrl
   
   put "key=value" into tPostData
   put "Content-Type: application/x-www-form-urlencoded" into tHeaders
   
   put tsNetPost("connectionA",tUrl,tHeaders,tPostData,"postResponse") into tError
end mouseUp

As with the standard libUrl HTTP post, we specify the URL to connect to, the data to POST to the server and any HTTP headers that we want to send in the request.  The default "Content-Type" header for POST requests with tsNet is "application/x-www-form-urlencoded", however this script sets that header as an example.

The first parameter to the tsNetPost function is a user defined value that is sent as the first parameter to the callback function which must be sent (by name) as the last parameter to the function (in this case - postResponse).  This allows each request to be uniquely identified.

The callback function is passed four parameters:

  1. pID - The value of this parameter is the user defined value passed as the first parameter to the tsNetPost function.  In this example - "connectionA"
  2. pStatus - For protocols that return a status code (notably HTTP and FTP), this is the status code returned by the server to the last command issued as part of the request.  While HTTP requests usually consist of only one command, FTP requests may include multiple commands (changing directory, logging out, etc....).
  3. pBytes - This is the number of bytes that have been transferred
  4. pResult - This is the cURL error code that is returned by the underlying library behind tsNet.  For successful connections, this will always be 0.  You can obtain the full list of possible errors at https://curl.haxx.se/libcurl/c/libcurl-errors.html or obtain a more descriptive error by calling tsNetRetrErr with the value of the pID parameter.

Here is the callback function for this example:

on postResponse pID, pStatus, pBytes, pResult
   local tError, tData, tResponse
   
   if pResult is not 0 then
      -- If pResult is not 0, then a libcurl error occurred, call tsNetRetrError() to get more detail of the error
      put tsNetRetrError(pID) into tError
      answer "Error:" && tError && "while retrieving data for entry" & pID
   else if the first char of pStatus is not 2 then
      -- Successful HTTP transactions get a 2xx response code, so error if this is not the case
      answer "Received HTTP response code" && pStatus && "while retreiving data for entry" & pID
   else
      -- Otherwise we have a successful transaction, so retrieve any data that was returned
      put tsNetRetrData(pID, tError) into tData
      if tError is not empty then
         -- This should only happen if the external ran out of memory retrieving the data, or the connection was already closed
         answer "Error:" && tError && "while retrieving data for entry" & pID
      end if
   end if
   
   -- Always close the connection to release any memory allocated to the connection
   tsNetCloseConn pID
   
   answer tData
end postResponse

Firstly the function checks to see if any cURL error occurred.  If so, it retrieves more information about the error by calling tsNetRetrError (passing it the value of pID) and alerting the user of any information it finds.

If no error occurred, we check the status code returned by the HTTP server to ensure we received a successful HTTP response.

Next, assuming no errors and a successful status code, we retrieve the returned HTTP data by calling tsNetRetrData (again passing it the value of pID).

Lastly, we close the connection to release any memory associated with it.

Sample Stacks

Another example LiveCode stack that sends multiple asynchronous HTTP POST requests and displays the results once they have all completed can be found here: https://tinyurl.com/ybk226ex

9 Comments

Kim

Thanks. I have it working but I'm trying to improve my error handling. Do I need to write code to handle tErrors or is it adequate just to write code to handle errors identified by the callback function? Could there be a tError which meant that the callback function was never triggered?

Kim

I think that, over the weekend, I may have worked out the answer to my own question.
- a tError will occur if I mis-form the inputs to the tsNetPost function. As such, tError is equivalent to "the Result" in many other Livecode functions. It's something that I could use in de-bugging my tsNetPost script (if I was having problems, which I'm not).
- where-as the callback function will report back on errors that occurred despite my having correctly formed inputs to the tsNetPost function. This includes, but is not restricted to: time-outs that occurred because a connection could not be made to the target URL, and http error status reports that come back from the target URL.
Am I on the right track?

Charles Warwick

Hi Kim,

It sounds like you are on the right track. If you need any more information just let us know.

Charles.

simon

How do I differentiate if I want to post FORM-DATA instead of just DATA. I have a situation where I need to upload a picture...this is the cURL I need to request:

curl -X 'POST' \
'https://XXXX.intrtl.com/api/v2/photos/' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'photo_id=aaa' \
-F 'scene_id=ddd' \
-F 'visit_id=bbb' \
-F '[email protected];type=image/jpeg' \

I would really appreciate any light on this, I have been trying for 3 days now with no success...

Charles Warwick

Hi Simon,

Check the LiveCode dictionary for the "libURLMultipartFormData" function, it shows you to how to format "form data" for posting to a HTTP server.

For what you are trying to do, you will need to replace the last three lines of the handler in the example code above with something similar to the following:

put "aaa" into tPhotoID
put "ddd" into tSceneID
put "bbb" into tVisitID
put "" & "C:/Pic1.jpg" into tPhotoData

put libURLMultipartFormData \
(tPostData, "photo_id", tPhotoID, "scene_id", tSceneID, "visit_id", tVisitID, "photo_data", tPhotoData) \
into tError

put line 1 of tPostData into tHeaders
put line 2 to -1 of tPostData into tFormData

put tsNetPost("connectionA", tUrl, tHeaders, tFormData, "postResponse") into tError

I hope that this helps, please let me know if you have any more questions.

Thanks,

Charles

Matthias Rebbe

It seems that the underlying software of this lesson area is removing/deleting some special characters like the << >>. Therefore Charles sample script is missing the file tag in the line

put "" & "C:/Pic1.jpg" into tPhotoData

So the correct would be

put "<file>" & "C:/Pic1.jpg" into tPhotoData

simon

Charles & Matthias, many thanks for your answers. I got it sorted out and in the mean time I found out (confirmed by Panos) that there is a documentation error and libURLMultipartFormData and contrary to what the dictionary states the library works on mobile as well. I guess it could be helpful for other readers to know about this.

Fernando

How do you add headers?, I need to put to elements in the headers:

Content-Type: Text

Authorization: Bearer {JWT}

I would really appreciate any help.

Thanks

Panos Merakos

Hello Fernando,

The 3rd param passed to tsNetPost() is the headers. So you could do:

local tHeaders
put "..." into tHeaders
...
tsNetPost(..,..,tHeaders,.....)

Hope this helps.

Kind regards,
Panos
--

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.