为什么 Flutter Dart 应用程序在测试程序和 POSTMAN 没有时抛出 XMLHttpRequest 错误?

Why is Flutter Dart application throwing an XMLHttpRequest error when test programme and POSTMAN do not?

我有一个应用程序在托管 postgres 数据库的 http.get 虚拟机和使用 Hapi/node 的 API 上执行 http.get 请求。 VM 磁盘损坏并在重建后,http.get 现在会抛出 XMLHttpRequest 错误。但是,执行相同请求的测试程序工作正常,使用 POSTMAN 的测试 GET 也工作正常。我很困惑为什么会这样。

报错的代码如下(getScores()函数):

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
      }
    }
  }
}

调用“getScores()”时的输出是:

getScores.  Error is XMLHttpRequest error..

Hapi接口代码如下:

'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();

有效的测试飞镖程序如下:

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);
  }
}

这个程序的输出是:

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

POSTMAN 结果是:

根据我所读到的有关 XMLHttpRequest 错误的内容,我认为这是一个 CORS 问题,但相信我已在 Hapi 界面中禁用了 CORS。应用程序 class、测试程序和 POSTMAN 测试都早于 Ubuntu VM 重建,因此唯一改变的是 VM。 Hapi 代码已备份,因此也不应更改,虽然我不太记得上次备份是什么时候。

有没有人知道为什么测试程序和 POSTMAN 可以工作,但我的应用程序中的 DataManager class 却没有?

在Hapi的routes定义中,cors需要设置为true,如下所示。

const server = Hapi.server({
    port: xxxx,
    host: 'xxx.xxx.xxx.xxx',
    routes: {
        cors: true
    }
});