【问题标题】:How to Handle Firebase Auth exceptions on flutter如何处理 Flutter 上的 Firebase 身份验证异常
【发布时间】:2019-09-30 11:57:56
【问题描述】:

请问有谁知道如何在 Flutter 上捕获 Firebase Auth 异常并显示它们?

注意:我对控制台不感兴趣(catcherror((e) print(e))

我需要一些更有效的东西,例如“用户不存在”,这样我就可以将它传递给一个字符串并显示它。

几个月来一直在处理这个问题。

提前致谢。

我尝试将 print(e) 替换为 //errorMessage=e.toString();然后把它传递给一个函数,所有的努力都是徒劳的。

    FirebaseAuth.instance
              .signInWithEmailAndPassword(email: emailController.text, password: passwordController.text)
              .then((FirebaseUser user) {
                _isInAsyncCall=false;
            Navigator.of(context).pushReplacementNamed("/TheNextPage");

          }).catchError((e) {
           // errorMessage=e.toString();
            print(e);
            _showDialog(errorMessage);

            //exceptionNotice();
            //print(e);

我希望能够提取异常消息并将异常消息传递给一个对话框,然后我可以将其显示给用户。

【问题讨论】:

    标签: firebase flutter firebase-authentication


    【解决方案1】:

    如果您使用的是 firebase_auth: ^0.18.0,错误代码已更改!检查下一个答案。

    我只是自己编写了一种无需依赖平台的代码即可实现此目的的方法:

    这是可能的,因为 .signInWithEmailAndPassword 正确地抛出带有定义代码的错误,我们可以抓住这些代码来识别错误并以应该处理的方式处理事情。

    如果发生任何错误,以下示例将创建一个新的 Future.error,然后配置一个 Bloc 以将该数据铲到 Widget。

    Future<String> signIn(String email, String password) async {
      FirebaseUser user;
      String errorMessage;
      
      try {
        AuthResult result = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
        user = result.user;
      } catch (error) {
        switch (error.code) {
          case "ERROR_INVALID_EMAIL":
            errorMessage = "Your email address appears to be malformed.";
            break;
          case "ERROR_WRONG_PASSWORD":
            errorMessage = "Your password is wrong.";
            break;
          case "ERROR_USER_NOT_FOUND":
            errorMessage = "User with this email doesn't exist.";
            break;
          case "ERROR_USER_DISABLED":
            errorMessage = "User with this email has been disabled.";
            break;
          case "ERROR_TOO_MANY_REQUESTS":
            errorMessage = "Too many requests. Try again later.";
            break;
          case "ERROR_OPERATION_NOT_ALLOWED":
            errorMessage = "Signing in with Email and Password is not enabled.";
            break;
          default:
            errorMessage = "An undefined Error happened.";
        }
      }
    
      if (errorMessage != null) {
        return Future.error(errorMessage);
      }
    
      return user.uid;
    }
    

    【讨论】:

    • 我在这个答案中看到的唯一问题是,catch 中的“错误”并不总是包含“代码”,这意味着您的代码将因“Uncaught TypeError: e.get$code is not a functionError: TypeError: e.get$code is not a function" 所以你必须使用显式的 catch,比如:"on PlatformException catch(e)" 然后你会确定它确实包含你的代码可以在这个答案中处理那个开关。附加说明:您还需要包含“import 'package:flutter/services.dart' show PlatformException;”在您的导入中。
    • 你测试过吗?根据我的经验,导致 null 的 error.code 将调用 switch 块的“默认值”。
    • 我认为@Frank 的评论适用于 dart2js 版本。似乎没有一种简单的方法来检查错误是否具有 getter code。但你可以使用这样的助手:gist.github.com/ltOgt/04212037dca1d7a68b4e89dddc750bbd
    • 这是一个有效的错误代码列表:pub.dev/documentation/firebase_auth/latest/firebase_auth/…
    • 这些错误信息已过时。请参阅@corentinhoudayer 的回答。
    【解决方案2】:

    新答案 (18/09/2020)

    如果您使用的是firebase_auth: ^0.18.0,则错误代码已更改!

    例如:ERROR_USER_NOT_FOUND 现在是 user-not-found

    我找不到任何关于此的文档,所以我进入源代码并阅读了每个错误代码的 cmets。 (firebase_auth.dart)

    我没有在我的应用程序中使用所有错误代码(例如验证、密码重置...),但您会在此代码 sn-p 中找到最常见的错误代码:

    (处理新旧错误代码)

    String getMessageFromErrorCode() {
        switch (this.errorCode) {
          case "ERROR_EMAIL_ALREADY_IN_USE":
          case "account-exists-with-different-credential":
          case "email-already-in-use":
            return "Email already used. Go to login page.";
            break;
          case "ERROR_WRONG_PASSWORD":
          case "wrong-password":
            return "Wrong email/password combination.";
            break;
          case "ERROR_USER_NOT_FOUND":
          case "user-not-found":
            return "No user found with this email.";
            break;
          case "ERROR_USER_DISABLED":
          case "user-disabled":
            return "User disabled.";
            break;
          case "ERROR_TOO_MANY_REQUESTS":
          case "operation-not-allowed":
            return "Too many requests to log into this account.";
            break;
          case "ERROR_OPERATION_NOT_ALLOWED":
          case "operation-not-allowed":
            return "Server error, please try again later.";
            break;
          case "ERROR_INVALID_EMAIL":
          case "invalid-email":
            return "Email address is invalid.";
            break;
          default:
            return "Login failed. Please try again.";
            break;
        }
      }
    

    【讨论】:

    • 谢谢,很有用!但是,我认为,ERROR_EMAIL_ALREADY_IN_USE == email-already-in-use
    • @IvanPavliuk 你说得对,我忘了编辑我的帖子。 account-exists-with-different-credential 和 email-already-in-use 都可以工作。谢谢!
    • int 最新的 firebase_auth 版本以获取错误代码,它将是 this.code 而不是 this.errorcode ...除此之外它可以完美运行
    • @Emam 这是我使用自定义 errorCode 属性制作的自定义类。很高兴它有效!
    【解决方案3】:

    (21/02/20) 编辑:这个答案很旧,其他答案包含跨平台解决方案,因此您应该先查看他们的解决方案并将其视为后备解决方案。

    firebase auth 插件实际上还没有合适的跨平台错误代码系统,因此您必须独立处理 android 和 ios 的错误。

    我目前正在使用来自此 github 问题的临时修复:#20223

    请注意,因为它是一个 temp 修复程序,不要指望它作为永久解决方案完全可靠。

    enum authProblems { UserNotFound, PasswordNotValid, NetworkError }
    
    try {
      FirebaseUser user = await FirebaseAuth.instance.signInWithEmailAndPassword(
          email: email,
          password: password,
      );
    } catch (e) {
      authProblems errorType;
      if (Platform.isAndroid) {
        switch (e.message) {
          case 'There is no user record corresponding to this identifier. The user may have been deleted.':
            errorType = authProblems.UserNotFound;
            break;
          case 'The password is invalid or the user does not have a password.':
            errorType = authProblems.PasswordNotValid;
            break;
          case 'A network error (such as timeout, interrupted connection or unreachable host) has occurred.':
            errorType = authProblems.NetworkError;
            break;
          // ...
          default:
            print('Case ${e.message} is not yet implemented');
        }
      } else if (Platform.isIOS) {
        switch (e.code) {
          case 'Error 17011':
            errorType = authProblems.UserNotFound;
            break;
          case 'Error 17009':
            errorType = authProblems.PasswordNotValid;
            break;
          case 'Error 17020':
            errorType = authProblems.NetworkError;
            break;
          // ...
          default:
            print('Case ${e.message} is not yet implemented');
        }
      }
      print('The error is $errorType');
    }
    

    【讨论】:

    • 这是 firebase 身份验证登录代码的 try catch 中的错误处理部分
    • 错误消失了,但是当我输入错误的电子邮件地址时它仍然会中断(冻结)
    • 这可能是您自己的代码,或者您错误地实现了它。我认为这样的尝试捕获不会冻结您的应用
    【解决方案4】:

    在Auth Class中有这个功能:

    Future signUpWithEmailAndPassword(String email, String password) async {
        try {
          AuthResult result = await _auth.createUserWithEmailAndPassword(
            email: email,
            password: password,
          );
          FirebaseUser user = result.user;
          return user;
        } catch (e) {
          return e;
        }
      }
    

    上面的catch错误返回一个runTimeType的PlatformException和flutter中的一个PlatformException有3个属性检查here!


    在您的 Dart 文件中,在按钮侦听器上实现此功能:

    String error = "";
    
    dynamic result = await _auth.signUpWithEmailAndPassword(email, password);
    
    if (result.runtimeType == PlatformException) {
        if (result.message != null) {
            setState(() {
                error = result.message;
            });
        } else {
            setState(() {
                error = "Unknown Error";
            });
        }
    }
    

    【讨论】:

      【解决方案5】:

      扩展已接受的答案,我认为值得一提的是:

      1. firebase_auth 插件有 AuthException。
      2. 正如Github issue post 中指出的那样,Android 和 iOS 的错误代码可能相同。
      3. 如果您在非 UI 层中有此代码,则可以使用 rethrow 或更好地抛出您自己的格式化异常并在 UI 级别捕获这些异常(您将确切知道您将得到的错误类型) .

      try {
        AuthResult authResult = await FirebaseAuth.instance.signInWithCredential(credential);
        // Your auth logic ...
      } on AuthException catch (e) {
        print('''
          caught firebase auth exception\n
          ${e.code}\n
          ${e.message}
        ''');
      
        var message = 'Oops!'; // Default message
        switch (e.code) {
          case 'ERROR_WRONG_PASSWORD':
            message = 'The password you entered is totally wrong!';
            break;
          // More custom messages ...
        }
        throw Exception(message); // Or extend this with a custom exception class
      } catch (e) {
        print('''
          caught exception\n
          $e
        ''');
        rethrow;
      }
      

      【讨论】:

      • Firebase 特定错误也存在 PlatformException,例如 ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL
      【解决方案6】:

      可以使用 FirebaseAuthException 类处理异常。

      这是使用电子邮件和密码登录的代码:

      void loginUser(String email, String password) async {
          try {
            await _auth.signInWithEmailAndPassword(email: email, password:password);
          } on FirebaseAuthException catch (e) {
            // Your logic for Firebase related exceptions
          } catch (e) {
          // your logic for other exceptions!
      }
      

      您可以使用自己的逻辑来处理错误,例如显示警报对话框等。 创建用户也可以这样做。

      【讨论】:

        【解决方案7】:

        在 Dart 中,您可以使用 on 语法对不同的异常做出反应。由于 Firebase 使用自己的 PlatformException,您可以轻松地通过以下方式捕获它们:

          try {
              AuthResult result = await signUp(email, password);
          } on PlatformException catch (e) {
              print(e.message);
          } on Exception catch (e) {
              print(e);
          }
        

        PlatformException 带来了一个代码和一个可以显示在 UI 中的消息,即:

        PlatformException(ERROR_EMAIL_ALREADY_IN_USE, 该邮箱地址已被其他账号使用。, null)

        【讨论】:

          【解决方案8】:

          我也被这个问题困住了一段时间,我用所有可用的错误 here 创建了这个要点,并举了一个例子,涵盖了所有平台异常代码

          处理注册异常的示例

          Future<String> signUp(String email, String password, String firstName) async {
            FirebaseUser user;
          
            try {
              AuthResult result = await _auth.createUserWithEmailAndPassword(
                  email: email, password: password);
              user = result.user;
              name = user.displayName;
              email = user.email;
          
              Firestore.instance.collection('users').document(user.uid).setData({
                "uid": user.uid,
                "firstName": firstName,
                "email": email,
                "userImage": userImage,
              });
            } catch (error) {
              switch (error.code) {
                case "ERROR_OPERATION_NOT_ALLOWED":
                  errorMessage = "Anonymous accounts are not enabled";
                  break;
                case "ERROR_WEAK_PASSWORD":
                  errorMessage = "Your password is too weak";
                  break;
                case "ERROR_INVALID_EMAIL":
                  errorMessage = "Your email is invalid";
                  break;
                case "ERROR_EMAIL_ALREADY_IN_USE":
                  errorMessage = "Email is already in use on different account";
                  break;
                case "ERROR_INVALID_CREDENTIAL":
                  errorMessage = "Your email is invalid";
                  break;
          
                default:
                  errorMessage = "An undefined Error happened.";
              }
            }
            if (errorMessage != null) {
              return Future.error(errorMessage);
            }
          
            return user.uid;
          }
          

          处理登录异常的示例

          Future<String> signIn(String email, String password) async {
            FirebaseUser user;
            try {
              AuthResult result = await _auth.signInWithEmailAndPassword(
                  email: email, password: password);
              user = result.user;
              name = user.displayName;
              email = user.email;
              userId = user.uid;
            } catch (error) {
              switch (error.code) {
                case "ERROR_INVALID_EMAIL":
                  errorMessage = "Your email address appears to be malformed.";
                  break;
                case "ERROR_WRONG_PASSWORD":
                  errorMessage = "Your password is wrong.";
                  break;
                case "ERROR_USER_NOT_FOUND":
                  errorMessage = "User with this email doesn't exist.";
                  break;
                case "ERROR_USER_DISABLED":
                  errorMessage = "User with this email has been disabled.";
                  break;
                case "ERROR_TOO_MANY_REQUESTS":
                  errorMessage = "Too many requests. Try again later.";
                  break;
                case "ERROR_OPERATION_NOT_ALLOWED":
                  errorMessage = "Signing in with Email and Password is not enabled.";
                  break;
                default:
                  errorMessage = "An undefined Error happened.";
              }
            }
            if (errorMessage != null) {
              return Future.error(errorMessage);
            }
          
            return user.uid;
          }
          

          【讨论】:

            【解决方案9】:

            所以我今天遇到了这个问题,我没有硬编码要显示的错误消息,而是决定使用字符串操作并设法得到消息。

            目标是获取消息(] 之后的所有内容)。 示例:get this => 密码应至少为 6 个字符 从 this => [firebase_auth/weak-password] 密码应至少为 6 个字符。

            所以使用 try-catch 中的异常,我首先将其转换为字符串,然后将前 14 个字符(从 '[' 到 '/')替换为空,所以我只剩下 weak-password ] 密码应至少为 6 个字符。

            然后使用 ']' 模式的 split 函数在剩余的字符串中搜索 ']' 符号,并将整个字符串分成两部分,其中 ']' 符号的索引作为枢轴。这将返回一个包含两个字符串的列表; 'weak-password' 和 '密码至少应为 6 个字符'。使用索引 1 获取第二个字符串,即错误消息。

            e.toString().replaceRange(0, 14, '').split(']')[1]
            

            【讨论】:

            • 这非常有效。只是为了扩展它不是 firebaseAuth 错误我有if (e.runtimetype == FirebaseAuthException) String mssge = Quwaysims answer else string mssege = error logging in please try again
            【解决方案10】:

            我更喜欢创建 api 层响应和错误模型,并将 firebase 插件错误和响应对象包装在其中。对于使用电子邮件和密码登录,我有这个

            @override
              Future<dynamic> loginWithEmailAndPassword(String email, String password) async {
                try {
                  await _firebaseAuth.signInWithEmailAndPassword(
                      email: email, password: password);
                  return FirebaseSignInWithEmailResponse();
                } catch (exception) {
                  return _mapLoginWithEmailError(exception);
                }
              }
            
              ApiError _mapLoginWithEmailError(PlatformException error) {
                final code = error.code;
                if (code == 'ERROR_INVALID_EMAIL') {
                  return FirebaseSignInWithEmailError(
                      message: 'Your email is not valid. Please enter a valid email',
                      type: FirebaseSignInWithEmailErrorType.INVALID_EMAIL);
                } else if (code == 'ERROR_WRONG_PASSWORD') {
                  return FirebaseSignInWithEmailError(
                      message: 'Your password is incorrect',
                      type: FirebaseSignInWithEmailErrorType.WRONG_PASSWORD);
                } else if (code == 'ERROR_USER_NOT_FOUND') {
                  return FirebaseSignInWithEmailError(
                      message: 'You do not have an account. Please Sign Up to'
                          'proceed',
                      type: FirebaseSignInWithEmailErrorType.USER_NOT_FOUND);
                } else if (code == 'ERROR_TOO_MANY_REQUESTS') {
                  return FirebaseSignInWithEmailError(
                      message: 'Did you forget your credentials? Reset your password',
                      type: FirebaseSignInWithEmailErrorType.TOO_MANY_REQUESTS);
                } else if (code == 'ERROR_USER_DISABLED') {
                  return FirebaseSignInWithEmailError(
                      message: 'Your account has been disabled. Please contact support',
                      type: FirebaseSignInWithEmailErrorType.USER_DISABLED);
                } else if (code == 'ERROR_OPERATION_NOT_ALLOWED') {
                  throw 'Email and Password accounts are disabled. Enable them in the '
                      'firebase console?';
                } else {
                  return FirebaseSignInWithEmailError(
                      message: 'Make sure you have a stable connection and try again'
                      type: FirebaseSignInWithEmailErrorType.CONNECTIVITY);
                }
              }
            

            我从不从 firebase 返回 AuthResult。相反,我会收听onAuthStateChanged 流并在有变化时做出相应的反应。

            【讨论】:

              【解决方案11】:

              我在使用“Firebase auth”的颤振中遇到了同样的“firebse 平台异常:”错误 即使在传递参数中使用 try catch 和 trim() 方法也无法解决。

              问题是当您使用 main.dart 中的“运行”按钮运行应用程序时,它不会回调并捕获错误。

              解决方案:在 Vscode 终端中输入“Flutter run”(用于调试模式)。或“Flutter run --release”(用于发布模式)现在您不会遇到平台异常。

              【讨论】:

                【解决方案12】:

                我遇到了一个问题,我不希望出现“com.google.firebase.FirebaseException:发生内部错误。[无法解析主机“www.googleapis.com”:没有关联地址 with hostname ]" 这将向用户表明正在使用的后端是 firebase。因此,我只使用了 toString().replaceAll()

                  Future<void> signIn() async {
                final formState = _formkey.currentState;
                var _date = DateTime.now();
                
                if (formState!.validate()) {
                  emailFocus!.unfocus();
                  passwordFocus!.unfocus();
                  formState.save();
                  setState(() {
                    isloading = true;
                    _errorMessage = '';
                  });
                  try {
                    UserCredential user = await _firebaseAuth.signInWithEmailAndPassword(
                        email: _email, password: _password!);
                
                    SharedPreferences prefs = await SharedPreferences.getInstance();
                    prefs.setString('email', _email);
                
                    await FirebaseFirestore.instance
                        .collection('Users Token Data')
                        .doc(user.user!.uid)
                        .set({'Email': _email, 'Token': _token, 'Date': _date});
                
                    Navigator.pushNamedAndRemoveUntil(
                        context, RouteNames.homePage, (e) => false);
                  } on FirebaseAuthException catch (e) {
                    setState(() {
                      isloading = false;
                      _errorMessage = e.message.toString().replaceAll(
                          'com.google.firebase.FirebaseException: An internal error has' +
                              ' occurred. [ Unable to resolve host "www.googleapis.com":' +
                              "No address associated with hostname ]",
                          "Please Check Network Connection");
                    });
                    print(e.message);
                  }
                }
                

                } }

                如果您不想从错误消息中透露太多信息。

                【讨论】:

                  【解决方案13】:

                  我最近也遇到了这个错误,我发现.catchError() 回调没有在调试模式下被调用(当你点击 VSCode 中的Run-&gt;Start Debugging 按钮时)。

                  但是,当您输入 flutter run -d 时,.catchError() 方法将被回调,因为它未处于调试模式。

                  要获取您喜欢的模拟器的代码,请将这行代码粘贴到终端中:

                  instruments -s devices
                  

                  如果这不起作用,您也可以尝试粘贴:

                  xcrun simctl list
                  

                  ```.catchError()`` 方法将被调用,不像以前那样,里面的代码将按预期执行!

                  此外,应用程序不会再因PlatformException() 而崩溃,而是您会得到这样的日志:

                  [VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'uid' was called on null.
                  Receiver: null
                  

                  我在 Google 登录中也遇到过这个问题,其中 .catchError() 没有被调用!

                  总之,如果你在处理 Firebase Authentication 的错误时遇到了一些错误,你应该首先尝试通过终端运行。谢谢,我希望这会有所帮助!

                  【讨论】:

                    【解决方案14】:

                    试试这个,我有同样的问题,这段代码对我有用

                    catch (e) {
                                            ScaffoldMessenger.of(context)
                                                .showSnackBar(SnackBar(content: Text('Wrong UserName or Password')));
                                          }
                    

                    【讨论】:

                      【解决方案15】:
                         try {
                        final newuser = await _auth.createUserWithEmailAndPassword(
                            email: emailController.text, password: passwordController.text);
                        // print("Done");
                      } catch (e) {
                        print(e);
                        showDialog(
                          context: context,
                          builder: (BuildContext context) {
                            return AlertDialog(
                              title: new Text(e.message),
                              actions: <Widget>[
                                FlatButton(
                                  child: new Text("OK"),
                                  onPressed: () {
                                    Navigator.of(context).pop();
                                  },
                                ),
                              ],
                            );
                          },
                        );
                      }
                      

                      【讨论】:

                      • 因为这是一个老问题,考虑添加一些解释它是如何工作的以及为什么它更好。
                      猜你喜欢
                      • 2021-07-11
                      • 2023-03-09
                      • 1970-01-01
                      • 2021-05-10
                      • 2019-01-05
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多