I have an application that was working doing http.get requests on an Ubuntu virtual machine hosting a postgres DB and an API using Hapi/node. The VM disk become corrpupted and following the rebuild, the http.get now throws an XMLHttpRequest error. However, a test programme doing the same request works fine and a test GET using POSTMAN works fine as well. I'm stumped as to why this is the case.
The code that is throwing the error is as follows (the getScores() function):
import 'package:noxo/functions.dart' as func;
import 'dart:convert';
import 'package:http/http.dart' as http;
class DataManager {
Map<dynamic, dynamic> results = {}; // Return values from DB parameter query
double difficulty = 5; // Level of difficulty (1- easiest to 5-hardest)
// Address of API for data requests
final _apiAddress = 'http://192.168.1.201:4044/';
bool _httpAvailable = true; // Assume that http is available
int getHumanScore() {
func.debugPrint('httpAvailable is $_httpAvailable');
if (_httpAvailable && results.isNotEmpty) {
func.debugPrint('Returning HUMAN score');
return results['humanwin'];
} else {
return 0;
}
}
int getDrawScore() {
if (_httpAvailable && results.isNotEmpty) {
return results['draws'];
} else {
return 0;
}
}
int getComputerScore() {
if (_httpAvailable && results.isNotEmpty) {
return results['computerwin'];
} else {
return 0;
}
}
void setDifficulty(double value) {
func.debugPrint('Set difficulty = $value');
difficulty = value;
}
double getDifficulty() {
func.debugPrint('Get difficulty is $difficulty');
return difficulty;
}
void getScores(Function() updateScores) async {
if (_httpAvailable) {
// If we haven't had a previous http error - read the scores
try {
dynamic _parsedAddress = Uri.parse(_apiAddress);
final response = await http.get(_parsedAddress);
func.debugPrint(
'doing getScores. Address = $_apiAddress. statusCode = ${response.statusCode}');
Map decodedResponse = jsonDecode(response.body) as Map;
if (response.statusCode == 200) {
func.debugPrint('getScores response: $decodedResponse');
results = decodedResponse;
updateScores(); // Update scores on main.dart
} else {
throw Exception('Unable to fetch products from the REST API');
}
} catch (e) {
func.debugPrint('getScores. Error is $e.');
_httpAvailable = false; // Disable http checks because we had an error
results =
{}; // Return an empty map if the internet connection is not available
}
}
}
// Put data about scores into the database
Future<void> putScores(String resultPath) async {
// Only try to put the scores if http is available
if (_httpAvailable) {
try {
String address = '$_apiAddress$resultPath';
func.debugPrint('http address: $address');
var response = await http.post(Uri.parse(address));
func.debugPrint('http response: ${response.body.toString()}');
// Check for sucess, throw an error if it didn't work
if (response.statusCode == 200 ||
response.statusCode == 201 ||
response.statusCode == 204) {
return;
} else {
throw Exception(
'Unable to update results from the REST API. Status Code: ' +
response.statusCode.toString());
}
} catch (e) {
_httpAvailable = false; // Disable http requests
}
}
}
}
The output when "getScores()" is called is:
getScores. Error is XMLHttpRequest error..
The Hapi interface code is as follows:
'use strict';
const Hapi = require('@hapi/hapi');
const { options } = require('@hapi/hapi/lib/cors');
const Inert = require('@hapi/inert');
const HapiPostgresConnection = require('hapi-postgres-connection');
const path = require('path');
const debug = true;
const init = async () => {
const server = Hapi.server({
port: 4044,
host: '192.168.1.201',
//host: '0.0.0.0',
routes: {
cors: false
/*
{
origin: ['192.168.*'], // an array of origins or 'ignore'
headers: ['Authorization'], // an array of strings - 'Access-Control-Allow-Headers'
exposedHeaders: ['Accept'], // an array of exposed headers - 'Access-Control-Expose-Headers',
additionalExposedHeaders: ['Accept'], // an array of additional exposed headers
maxAge: 60,
credentials: false // boolean - 'Access-Control-Allow-Credentials'
*/
}
});
await server.register([{
plugin: HapiPostgresConnection
},
{
plugin: Inert
}]);
server.route({
method: 'GET',
path: '/',
handler: async function (request, h) {
let id = '1';
let statement = `SELECT * FROM scores WHERE id = ${id}`;
debugPrint(`Doing GET. Statement = ${statement}`);
try {
const result = await request.pg.client.query(statement);
debugPrint(`After GET. Result = ${result}. Response = ${h.response()}`);
return h.response(result.rows[0]);
} catch (err) {
console.log(err);
}
}
});
server.route({
method: 'GET',
path: '/noxo',
handler: (request, h) => {
return h.file('/home/mike/programming/noxo/index.html');
}
});
server.route({
method: 'GET',
path: '/fwebtest',
handler: (request, h) => {
return h.file('index.html', options [{confine: false}]);
},
options: {
files: {
relativeTo: path.join(__dirname, 'fwebtest')
},
},
});
server.route({
method: 'GET',
path: '/webtest',
handler: (request, h) => {
return h.file('./webtest/index.html', options [{confine: false}])
}
});
server.route({
method: ['PUT', 'POST'],
path: '/',
handler: async function (request, h) {
let statement = 'update scores set';
var jsonData = request.payload;
if (jsonData.hasOwnProperty('id')) {
delete jsonData.id;
}
var first = true
for (var key of Object.keys(jsonData)) {
if (!first) {
statement = statement + ",";
}
statement = statement + ' ' + key + ' = ' + jsonData[key];
first = false;
}
statement = statement + ' where id = 1'
debugPrint(`Doing PUT. Statement = ${statement}`);
try {
const result = await request.pg.client.query(statement);
return h.response(result.rows[0]);
} catch (err) {
console.log(err);
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
function buildStatement(element, index, array) {
if (index != 'id') {
statement = statement + index + ' = ' + value + ' ';
}
return this;
}
function debugPrint(value){
if (debug == true){
console.log(value);
}
}
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
The test dart programme, which works, is as follows:
import 'dart:convert';
import 'package:http/http.dart' as http;
// Address of API for data requests
final _apiAddress = 'http://192.168.1.201:4044/';
// Global variables
bool _httpAvailable = true; // Has http conect worked? Assume yes.
const bool debug = true; // Global bebug print variable.
void main() async {
// Get the scores from the API
Map scores = await _getScores();
}
// Function to get the scores using http command
Future<Map> _getScores() async {
debugPrint('Doing GET...');
dynamic response;
Map decodedResponse = {};
try {
dynamic _parsedAddress = Uri.parse(_apiAddress);
response = await http.get(_parsedAddress);
decodedResponse = jsonDecode(response.body) as Map;
} catch (e) {
debugPrint('getScores. Error is $e.');
_httpAvailable = false; // Disable http checks because we had an error
decodedResponse =
{}; // Return an empty map if the internet connection is not available
}
if (response.statusCode == 200) {
debugPrint('response.body:');
debugPrint(response.body);
debugPrint('GET successful... code: ' + response.statusCode.toString());
debugPrint(' ');
return decodedResponse;
} else {
throw Exception('Unable to fetch products from the REST API. Error code: ' +
response.statusCode.toString());
}
}
debugPrint(String message) {
if (debug == true) {
print(message);
}
}
The output from this programme is:
D:\Sync\Programming\Flutter\http_get>dart run
Building package executable...
Built http_test:http_test.
Doing GET...
response.body:
{"id":1,"humanwin":3,"draws":0,"computerwin":0}
GET successful... code: 200
The POSTMAN results are:
I assume that this is a CORS issue from what I have read about XMLHttpRequest errors, but believe I have disabled CORS in the Hapi interface. The application class, test programme and POSTMAN test all pre-date the Ubuntu VM rebuild so the only thing that has changed is the VM. The Hapi code was backed up so that shouldn't have changed either, although I can't quite remember when I took the last backup.
Does anyone have any ideas about why the test programme and POSTMAN work, but my DataManager class in my application doesn't?
In the routes
definition for Hapi, cors
needs to be set to true
as below.
const server = Hapi.server({
port: xxxx,
host: 'xxx.xxx.xxx.xxx',
routes: {
cors: true
}
});