22/01/2021
This post explains how to make a HTTP POST call and process the response.
To use our services your game must be able to make a HTTP POST request and process the response sent back.
Luckily, most modern languages, and newer versions of older languages, now incorporate the means to do this (or there are libraries that can be imported).
The LUA language, used by the Solar2D games engine, is one such language that has the functionality to make HTTP POST requests available out of the box.
Here is the example LUA code we will explain in this post:
--------------------------------------------------------------------------
local json = require("json")
local function pingListener( event )
print("==============")
print(" [ Ping ]")
print("--------------")
if ( event.isError ) then
print( "Network error: ", event.response )
else
print ( event.response )
end
print("==============")
end
local function ping()
local headers = {}
headers["Content-Type"] = "application/json"
headers["Accept-Language"] = "en-GB"
local body = {}
body.api_key = highscores_api_key
body.instanceId = instanceId
local params = {}
params.headers = headers
params.body = json.encode(body)
network.request( "https://www.my-api-endpoint.com/Ping", "POST", pingListener, params )
end
--------------------------------------------------------------------------
In this post we shall explain how to make a call to a method called "Ping" using LUA.
The "Ping" method requires two values to be passed to it: "instanceId" and "api_key".
If you are a subscriber to our services you will know what these two values are, otherwise it is suffice to say they are two strings values.
The "Ping" method will return zero (0) to indicate that the service is online and accepting calls using the two values passed, or it returns an error or a timeout.
To make this call in LUA, we use the "network.request" method.
[ Note: The "network" library that the "request" method belongs to is loaded automatically so we do NOT need to import it with the "required" command. ]
The method requires the following parameters to be passed:
network.request( url, type, listener, values )
1) url: The full web-address to the method we want to call e.g. https://www.my-api-endpoint.com/Ping
2) type: The type of request: "POST"
3) listener: A "listening" function that will run in the background waiting for a response (or a timeout event) and do something with it (more about that in a moment)
4) values: A list of parameter values to be passed with the request
The first two parameters are self explanatory.
The fourth is an array we have called "params":
local params = {}
"params" has two elements:
1) headers
2) body
Both parameters are arrays, and we define these arrays in the code BEFORE we assign them to the "params" array, defining them later will result in a "not found" runtime error.
We define an array called "header". This is a reserved name used to pass through HTTP request header values. For our requirements, we only need to define two header values:
1) "Content-Type" - this tells the receiving service to expect the data passed in the "params" array to be in JSON format
2) "Accept-Language" - this tells the receiving service to expect the language of the data to be GB English
[ Note: We define these two elements using the notation "array[element] = ". We do this because the names contain "-"; if we attempt to assign the values using the notation "array.element = " it will fail as the runtime will attempt to perform an operation on the two sides of the "-" ]
We define another array called "body" (it can be called anything, we have called the array the same as the "params" element for easy identification):
local body = {}
We then declare two elements in the array, one called "api_key", and another called "instanceId" (these are the two parameters that the "Ping" method will be expecting).
With the two arrays defined, we can then assign them to the "params" array:
local params = {}
params.headers = headers
params.body = json.encode(body)
Note that we encode the "body" array to JSON (as we said we would in the header array's "Content-Type"):
json.encode(body)
For "encode" to be recognised we have to "import" the "json" library into our module. We do this at the top of the module with this directive:
local json = require("json")
When "local function ping()" is run the "network.request" is executed, a request is passed to the URL with the values contained in the "params" array.
LUA operates asynchronously, that is to say, it does not wait for "network.request" to receive a response or for a timeout event to occur. Instead, it gets on and does other things, like running your game.
What it does instead is it sets up the listening function to run in the background waiting for a response or a timeout.
For our "Ping" request, we have written the local function "pingListener".
Just like the arrays, we have to declare this function BEFORE we use it in the "network.response" method in the main "Ping" function.
This function stores the response in "event". This is of "Http Response" type.
When a response is returned, or a timeout event is triggered, "event" will contain the information we need to continue.
"event" has two properties that we are interested in:
1) "isError"
2) "response"
The first element "isError" is a boolean value.
If it is "true", then the call was NOT successful. There was a NETWORK error.
In our listening function, if this happens, we print the contents of the "response" property and prefix it with "Network error: ".
If "isError" is false, then the property "response" will contain a response from the service.
For our "Ping" method, a successful response is a JSON {array}. It will look like this when printed out by the listening function:
{"status":0,"errMsg":""}
It will contain two name/value pairs: "status" and "errMsg".
In this example, status is "0" and errMsg is empty.
If either of the two parameters passed with the request ("instanceId" and "api_key") were wrong, then "Ping" would instead return:
{"status":1,"errMsg":"Invalid credentials"}
Either way, we could then assign the values of the two elements using the JSON "decode" method:
local r = json.decode(event.response)
Then extract the values as you would any {array} in LUA:
local status = r.status
local errMsg = r.errMsg
Finally, if there was an error with the service itself, maybe a runtime error, then "response" would not be a JSON {array} but a string of HTML code of the error's web-page that can be copy & pasted into a HTML file and opened/viewed in a web-browser (in this case "status" would not exist, and you can test for that).
There! We hope this post helps you understand how LUA can raise a HTTP POST request and handle the response.