从 Flutter 应用程序连接到本地 Firebase 函数模拟器时出错 [英] Error connecting to local Firebase functions emulator from Flutter app

查看:23
本文介绍了从 Flutter 应用程序连接到本地 Firebase 函数模拟器时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用本地 Firebase 函数模拟器作为我的后端设置我的项目并从我的 Android 模拟器调用我的 Firebase onCall 函数后,我收到这个非常不具信息性的错误消息 PlatformException(functionsError, Cloud function failed with exception., {code: INTERNAL, details: null, message: INTERNAL}).完整的错误信息如下:

After setting up my project with a local Firebase functions emulator as my backend, and calling my Firebase onCall function from my Android emulator, I get this very non-informative error message PlatformException(functionsError, Cloud function failed with exception., {code: INTERNAL, details: null, message: INTERNAL}). Full error message below:

E/flutter (20862): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(functionsError, Cloud function failed with exception., {code: INTERNAL, details: null, message: INTERNAL})
E/flutter (21445): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
E/flutter (21445): #1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:156:18)
E/flutter (21445): <asynchronous suspension>
E/flutter (21445): #2      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:329:12)
E/flutter (21445): #3      MethodChannelCloudFunctions.callCloudFunction (package:cloud_functions_platform_interface/src/method_channel_cloud_functions.dart:43:15)
E/flutter (21445): #4      HttpsCallable.call (package:cloud_functions/src/https_callable.dart:33:12)
E/flutter (21445): #5      ApiService.loadUserLessonsByLessonIds (package:kim/services/api.dart:28:21)
E/flutter (21445): #6      MapScreen.build.<anonymous closure> (package:kim/screens/map.dart:170:34)
E/flutter (21445): #7      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:779:19)
E/flutter (21445): #8      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:862:36)
E/flutter (21445): #9      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
E/flutter (21445): #10     TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:504:11)
E/flutter (21445): #11     BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:282:5)
E/flutter (21445): #12     BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:217:7)
E/flutter (21445): #13     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9)
E/flutter (21445): #14     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:76:12)
E/flutter (21445): #15     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:122:9)
E/flutter (21445): #16     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)
E/flutter (21445): #17     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:120:18)
E/flutter (21445): #18     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:106:7)
E/flutter (21445): #19     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:218:19)
E/flutter (21445): #20     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:198:22)
E/flutter (21445): #21     GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
E/flutter (21445): #22     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)
E/flutter (21445): #23     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)
E/flutter (21445): #24     _rootRunUnary (dart:async/zone.dart:1196:13)
E/flutter (21445): #25     _CustomZone.runUnary (dart:async/zone.dart:1085:19)
E/flutter (21445): #26     _CustomZone.runUnaryGuarded (dart:async/zone.dart:987:7)
E/flutter (21445): #27     _invoke1 (dart:ui/hooks.dart:275:10)
E/flutter (21445): #28     _dispatchPointerDataPacket (dart:ui/hooks.dart:184:5)
E/flutter (21445): 

服务器代码index.ts:

import * as functions from 'firebase-functions'

interface LoadUserLessonsData {
  lessonIds: string[]
}

export const loadUserLessons = functions.https.onCall((data: LoadUserLessonsData, context) => {
  const uid = context.auth?.uid
  const ids = data.lessonIds
  console.log(uid, ids)
})

运行firebase emulators:start --only functions后的服务器控制台窗口:

The server console window after running firebase emulators:start --only functions:

...
+  functions[loadUserLessons]: http function initialized (http://localhost:5001/project-name/us-central1/loadUserLessons).
...
┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator  │ Host:Port      │ View in Emulator UI             │
├───────────┼────────────────┼─────────────────────────────────┤
│ Functions │ localhost:5001 │ http://localhost:4000/functions │
└───────────┴────────────────┴─────────────────────────────────┘

客户端应用代码api.dart:

import 'dart:io';

import 'package:cloud_functions/cloud_functions.dart';
import 'package:myapp/services/config.dart';

class ApiService {
  static final ApiService _apiService = ApiService._internal();
  static final _functions = CloudFunctions.instance;

  ApiService._internal();

  factory ApiService() {
    init();
    return _apiService;
  }

  static void init() {
    // 10.0.2.2 is the special IP address to connect to the 'localhost' of the host computer from an Android emulator.
    final origin = Platform.isAndroid ? 'http://10.0.2.2:5001' : 'http://localhost:5001';
    _functions.useFunctionsEmulator(origin: origin);
  }

  static Future<dynamic> loadUserLessons(List<String> lessonIds) {
    final HttpsCallable callable = _functions.getHttpsCallable(
      functionName: 'loadUserLessons',
    );
    return callable.call({
      'lessonIds': lessonIds,
    });
  }
}

如何正确设置 Flutter 应用以连接到本地模拟器?

What can I do to correctly setup my Flutter app to connect to my local emulator?

需要注意的是,无论模拟器是否运行,错误信息都是一样的.模拟器也不会在控制台日志中注册任何事件.但是,当模拟器运行时,向函数端点发送自定义请求(例如,通过REST Api Client"之类的 Android 应用程序)确实会使模拟器做出反应.

It should be noted that the error message is the same regardless if the emulator is running or not. The emulator doesn't register any events in the console logs either. However, when the emulator is running, sending an custom request (e.g. through an Android app like "REST Api Client") to the function endpoint does make the emulator react.

这意味着问题仅出在 Flutter 代码中(我的或 cloud_functions Flutter 包).至少,这是我目前的结论.

This means that the problem is solely in the Flutter code (mine or cloud_functions Flutter package). At least, that's my conclusion so far.

推荐答案

幸运的是,我设置了 Crashlytics,得到了更详细的错误消息,其中之一是:

Luckily I had set up Crashlytics that had been given more detailed error messages, one of which was:

CLEARTEXT communication to 10.0.2.2 not permitted by network security policy

这让我想到了这个 Stackoverflow 问题和 Ashish John 的评论:

which pointed me to this Stackoverflow question and the comment made by Ashish John:

OkHttp: <-- HTTP FAILED: java.net.UnknownServiceException:网络安全策略不允许与 10.0.2.2 进行 CLEARTEXT 通信

你可以'android:usesCleartextTraffic=true"'在清单文件的应用程序"标签中.如果您的 API/Link 不支持 https &您使用的是Android P"或更高版本.

You can you 'android:usesCleartextTraffic="true"' in 'Application' tag in manifest file. This problem occurs if your API/Link doe not support https & you are using 'Android P' or above.

阅读更多关于 android:usesCleartextTraffic https://developer.android.com/guide/topics/manifest/application-element

指示应用程序是否打算使用明文网络流量,例如明文 HTTP.面向 API 级别 27 或更低级别的应用的默认值为true".以 API 级别 28 或更高级别为目标的应用默认为false".

Indicates whether the app intends to use cleartext network traffic, such as cleartext HTTP. The default value for apps that target API level 27 or lower is "true". Apps that target API level 28 or higher default to "false".

换句话说,如果您的 Android 模拟器运行 Android API 级别 28 或更高版本,您需要将 android:usesCleartextTraffic=true" 添加到 <application> 应用的 AndroidManifest.xml

In other words, if your Android emulator is running Android API level 28 or higher you need to add android:usesCleartextTraffic="true" to the <application> tag in your app's AndroidManifest.xml

现在我们需要允许 Android 模拟器和 Firebase 函数模拟器之间的流量,Ahmed Ghrib 解决了这个问题.

Now we need to permit the traffic between Android emulator and the Firebase functions emulator, which Ahmed Ghrib solved.

OkHttp: <-- HTTP FAILED:java.net.UnknownServiceException:网络安全策略不允许与 10.0.2.2 进行 CLEARTEXT 通信

创建一个文件 network_security_config.xml 并将其放在 [PROJECT]/android/app/src/main/res/xml 中,代码如下:

Create a file network_security_config.xml and place it in [PROJECT]/android/app/src/main/res/xml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain>10.0.2.2</domain>
    </domain-config>
</network-security-config>

这是对 Ahmed Ghrib 回答的修改版本,以限制明文流量只能在 Android 模拟器和 Firebase 函数模拟器之间使用.

This is a modified version made to Ahmed Ghrib's answer, to limit clear text traffic to only be used between the Android emulator and the Firebase functions emulator.

这篇关于从 Flutter 应用程序连接到本地 Firebase 函数模拟器时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆