简体   繁体   English

如何使用 Flutter 防御性地从 Firestore 请求数据

[英]How to request data from Firestore defensively with Flutter

Since Firestore is a NoSQL database and has no strict type rules and defined document structures, I think about handling corrupt data in my Flutter app.由于 Firestore 是一个 NoSQL 数据库,没有严格的类型规则和定义的文档结构,我考虑在我的 Flutter 应用程序中处理损坏的数据。

In case you wonder why I want to request defensively, even when it is no third-party API -> I can think of three reasons why my app crashes because of corrupt data:如果您想知道为什么我要防御性请求,即使它不是第三方 API -> 我可以想到我的应用程序因数据损坏而崩溃的三个原因:

  • I add wrong data via the Firebase console like using type string for a field that should have been type number (happened repeatedly).我通过 Firebase 控制台添加了错误的数据,例如对本应为number类型的字段使用类型string (重复发生)。
  • A bug in my app adds corrupt data to Firestore.我的应用程序中的错误会将损坏的数据添加到 Firestore。
  • A user has an old version of my app installed which can not handle the new Firestore data structure.用户安装了我的应用程序的旧版本,无法处理新的 Firestore 数据结构。

My requirements: The app should not crash when corrupt data from Firestore is requested, but the corrupt data should be reported so that the data can be fixed in Firestore asap我的要求:当请求来自 Firestore 的损坏数据时,应用程序不应崩溃,但应报告损坏数据,以便尽快在 Firestore 中修复数据

What do you think about the following approach?您如何看待以下方法?

Assume we have a model Movie .假设我们有一部 model Movie

Movie {
  final String title;
  final int releaseYear;

  Movie({required this.title, required this.releaseYear});

  Movie.from(Map<String, dynamic> data)
      : title = data['title'],
        releaseYear = data['release_year'];
}

The named constructor from parses the document data from DocumentSnapshot.data() and returns our model. This works fine as long as the data has a field title of type String and a field release_year of type int ( number in Firestore).命名构造函数from解析来自DocumentSnapshot.data()的文档数据并返回我们的 model。只要数据具有String类型的字段titleint类型的字段release_year (Firestore 中的number ),这就可以正常工作。

Let's assume the field release_year is missing in the actual data.假设实际数据中缺少字段release_year This will let the request crash.这会让请求崩溃。 Thus, the current user can't do anything with the respective movie and I as developer won't notice, because it happened silently on the device of the user.因此,当前用户不能对相应的电影做任何事情,作为开发者我也不会注意到,因为它是在用户的设备上悄悄发生的。

To fix the first issue, we can use defensive parsing with fallback data like this: data['release_year']?? -1要解决第一个问题,我们可以像这样对后备数据使用防御性解析: data['release_year']?? -1 data['release_year']?? -1 . data['release_year']?? -1 No crash happens, but I as developer still don't notice and can't fix the data.没有发生崩溃,但我作为开发人员仍然没有注意到并且无法修复数据。

To fix also this issue we could use Firebase Crashlytics.为了解决这个问题,我们可以使用 Firebase Crashlytics。 The only problem is that if we use defensive parsing to prevent crashing no log will be sent to Firebase. That's why I came up with this solution:唯一的问题是,如果我们使用防御性解析来防止崩溃,则不会将任何日志发送到 Firebase。这就是我想出这个解决方案的原因:

final snapshot = await FirebaseFirestore.instance.collection('movies').doc('123').get();
try {
  return Movie.from(snapshot.data()!);
} catch (e) {
  await FirebaseCrashlytics.instance
      .recordError(e, e.stackTrace(), reason: 'data of movie ${snapshot.id} is corrupt');
  return Movie.fromCorrupt(snapshot.data()!);
}

First, the app tries to parse the document data without any fallback mechanism.首先,应用程序尝试在没有任何回退机制的情况下解析文档数据。 If the data is corrupt an exception is thrown and catched.如果数据损坏,则会抛出并捕获异常。 In the catch block, the error is send to Firebase and then the defensive parsing constructor fromCorrupt is called to let the user continue in the app with the remaining data.在 catch 块中,错误被发送到 Firebase,然后调用防御性解析构造函数fromCorrupt让用户继续使用剩余数据在应用程序中。 In fromCorrupt each field is checked on null and type, before it is used to create the model Movie .fromCorrupt ,每个字段都在 null 和类型上进行检查,然后才用于创建 model Movie If a value is null or of a wrong type, a fallback value is used.如果值为 null 或类型错误,则使用回退值。

What do you think of my approach?你觉得我的方法怎么样? Am I overengineering?我是不是过度工程化了?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM