HTML/JS: How we Send and Receive JSON data from APL Server

General APL language issues

HTML/JS: How we Send and Receive JSON data from APL Server

Postby PGilbert on Sat May 22, 2021 12:50 pm

Here are the functions that we are using to Send and Receive JSON data from an APL server:

To send JSON data:

Code: Select all
function sendJSON(jsObject) {
    // From: https://developers.google.com/web/updates/2015/03/introduction-to-fetch
    // https://developers.google.com/web/updates/2017/09/abortable-fetch
    // https://developers.google.com/web/ilt/pwa/working-with-the-fetch-api
    // Function to send data to APL without any data in return.
    // With POST it is permitted to send JSON data in the 'body' property.
    // Usage:
    //sendJSON(r)
    //    .then(() => ) // success
    //    .catch(() => ) // failure
    //    .finally(() => ) // always

    // Convert the Javascript Object(jsObject) to a JSON Object (jsonObject).
    let jsonString;
    try {
        if (isString(jsObject)) {
            // If the string will make an error when parsed by Apl,
            // it will return a statusCode of 400 for bad request.
            jsonString = jsObject;
        } else {
            // The following line will always work because a faulty object argument will
            // make a syntax error before calling sendJSON.
            jsonString = JSON.stringify(jsObject);
        }
    } catch (error) {
        //console.error(`getJSON: Ill formed request:\nRequest: ${jsObject}\nError: ${error.message}`);
        //return Promise.reject(new Error(`sendJSON: Ill formed request:\nRequest: ${jsonString}\nError: ${error.message}`));
        // The following line should never be called because there no error that will go to here.
        // Erase if it is the case.
        let catchError = `{"name":"Parsing Error","responseText":"Error parsing ${jsObject}","sender":"getJSON","statusCode":0,"statusText":"${error.message}"}`;
        return new Error(catchError);
    }

    // Prepare an AbortController that will time-out if no response from Apl.
    // There is no time-out option in fetch, has to be made with code.
    let timeOut;
    if ('TimeOut' in jsObject) {
        timeOut = jsObject.TimeOut;
    } else {
        timeOut = 750; // default 0.75 second time-out
    }
    const controller = new AbortController();
    setTimeout(() => {
        controller.abort()
    }, timeOut);

    // Make the call using javascript 'fetch'.
    return fetch(_URL, {
        method: 'POST',
        body: jsonString,
        signal: controller.signal
    }).then((response) => {
        // if (response.status >= 200 && response.status < 300) { // same as line below.
        if (response.ok) {
            return Promise.resolve(response)
        } else {
            //return Promise.reject(new Error(response.statusText))
            return response.text().then(text => {
                return Promise.reject({
                    name: "notAbortError",
                    responseText: text,
                    sender: "?", // to be completed
                    statusCode: response.status,
                    statusText: response.statusText
                });
            });
        }
    }).catch((error) => {
        // When the AbortController time-out it will create an Error with name 'AbortError'.
        // Otherwise it will read the error made in the above Promise.reject.
        // The constructor of Error only accept characters and not an object to be passed.
        // Those characters can be read back with the property .message of the Error object.
        if (error.name === 'AbortError') {
            // The AbortController has time-out and throw the 'AbortError'.
            // Next line does not work without 'throw'.
            throw new Error('{"name":"AbortError","responseText":"No Response From Server","sender":"?","statusCode":408,"statusText":"Request Timeout"}')
        } else {
            // Error from the above Promise.reject.
            throw new Error(JSON.stringify(error))
        }
    })
}


To request some JSON data from an APL server:

Code: Select all
function getJSON(jsObject) {
    // Function to get data from APL server.
    // With GET it is not permitted to send JSON data in the 'body' property.
    // Usage:
    //  getJSON(r)
    //    .then(() => ) // success
    //    .catch(() => ) // failure
    //    .finally(() => ) // always

    // https://codereview.stackexchange.com/questions/173538/timeout-implementation-of-fetch (don't understand)
    // https://www.lowmess.com/blog/fetch-with-timeout/ (working)

    // Convert the Javascript Object(jsObject) to a JSON Object (jsonObject).
    let jsonString;
    try {
        if (isString(jsObject)) {
            // If the string will make an error when parsed by Apl,
            // it will return a statusCode of 400 for bad request.
            jsonString = jsObject;
        } else {
            // The following line will always work because a faulty object argument will
            // make a syntax error before calling getJSON.
            jsonString = JSON.stringify(jsObject);
        }
    } catch (error) {
        //console.error(`getJSON: Ill formed request:\nRequest: ${jsObject}\nError: ${error.message}`);
        //return Promise.reject(new Error(`getJSON: Ill formed request:\nRequest: ${jsonString}\nError: ${error.message}`));
        // The following line should never be called because there no error that will go to here.
        // Erase if it is the case.
        let catchError = `{"name":"Parsing Error","responseText":"Error parsing ${jsObject}","sender":"getJSON","statusCode":0,"statusText":"${error.message}"}`;
        return new Error(catchError);
    }

    let url = _URL + "&request=" + encodeURIComponent(jsonString);

    // Prepare an AbortController that will time-out if no response from Apl.
    // There is no time-out option in fetch, has to be made with code.
    let timeOut;
    if ('TimeOut' in jsObject) {
        timeOut = jsObject.TimeOut;
    } else {
        timeOut = 750; // default 0.75 second time-out
    }
    const controller = new AbortController();
    setTimeout(() => {
        controller.abort()
    }, timeOut);

    // Fetch call is made here.
    return fetch(url, {
        method: 'GET',
        signal: controller.signal
    }).then((response) => {
        // if (response.status >= 200 && response.status < 300) { // same as line below.
        if (response.ok) {
            return Promise.resolve(response.json()) // JSON.Parse the answer.
        } else {
            //return Promise.reject(new Error(response.statusText)) // ←←← not possible to get Apl error with this code
            // response.text will get us the error sent from Apl.
            return response.text().then((text) => { // no parsing.
                return Promise.reject({
                    name: "notAbortError",
                    responseText: text,
                    sender: "?", // to be completed
                    statusCode: response.status,
                    statusText: response.statusText
                });
            });
        }
    })
        .catch((error) => {
            // When the AbortController time-out it will create an Error with name 'AbortError'.
            // Otherwise it will read the error made in the above Promise.reject.
            // The constructor of Error only accept characters and not an object to be passed.
            // Those characters can be read back with the property .message of the Error object.
            if (error.name === 'AbortError') {
                // The AbortController has time-out and throw the 'AbortError'.
                // Next line does not work without 'throw'.
                throw new Error('{"name":"AbortError","responseText":"No Response From Server","sender":"?","statusCode":408,"statusText":"Request Timeout"}')
            } else {
                // Error from the above Promise.reject.
                throw new Error(JSON.stringify(error))
            }
        })


Comments and suggestions for improvements are welcome.
User avatar
PGilbert
 
Posts: 436
Joined: Sun Dec 13, 2009 8:46 pm
Location: Montréal, Québec, Canada

Return to Language

Who is online

Users browsing this forum: No registered users and 1 guest