【问题标题】:Firebase Java Android Check if a child was not created because it was the same as another already present in the same parentFirebase Java Android 检查是否未创建子项,因为它与同一个父项中已经存在的另一个子项相同
【发布时间】:2021-08-21 10:42:12
【问题描述】:

我正在为 Android 应用程序开发一个使用 java 的登录和注册系统。我使用firebase用电子邮件和密码注册用户,电子邮件、用户名和其他信息将在实时数据库firebase上形成一个继承结构,因为父级有用户ID:

但是,我希望用户名是唯一的,这样如果用户编写的用户名已经存在于父用户名中,他就会识别出另一个孩子是相同的。由于 firebase 不允许您创建具有相同名称的孩子,因此我尝试执行以下操作:

FirebaseDatabase.getInstance().getReference("Usernames").child(us.getText().toString()).setValue("Saved").addOnCompleteListener(new OnCompleteListener<Void>() {
                        @Override
                        public void onComplete(@NonNull Task<Void> task) {
                            Toast.makeText(getApplicationContext(), "Not same username", Toast.LENGTH_SHORT).show();
                        }
                    }).addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Toast.makeText(getApplicationContext(), "same username", Toast.LENGTH_SHORT).show();
                        }
                    });

但我不明白为什么,答案总是 OnCompleted,即使孩子与另一个孩子同名,因此用户输入了已输入的用户名。

有人能告诉我如何检查 Firebase 数据库是否因为与另一个名称相同而没有插入子节点

这是我当前的数据库规则:

{
  "rules": {
    ".read": "true",  
    ".write": "true",
  }
}

完整代码:https://codeshare.io/Gq880j

【问题讨论】:

  • 不要用答案中的代码修改原始问题中的代码,保持原样。
  • 好的@samthecodingman

标签: java firebase firebase-realtime-database children


【解决方案1】:

您可以像这样检查用户名是否存在:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference("Usernames").child(us.getText().toString());
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  void onDataChange(DataSnapshot snapshot) {
    if (snapshot.exists()) {
      // Username is already taken
    } else {
      // Username is available, add the data
    }
  }
});

这可能是最简单的方法。如果直接使用更新操作,它会覆盖现有值。

【讨论】:

  • 我试图添加你的代码(看看问题,我用我添加的代码更新了代码)但它每次都给我“我们已经是前任”......我不明白为什么
  • @pophome 请分享您数据库的完整屏幕截图,可能路径错误。
  • @pophome 我刚刚注意到我忘了删除 setValue。你可以试试更新后的代码吗?
  • 我已经尝试了您更新的代码,但它继续输出在所有情况下都没有使用的用户名敬酒
  • @pophome 这很奇怪,你可以尝试硬编码一个名称而不是 us.getText() 吗?所以我们可以确认输出没问题。此外,它区分大小写。所以myNaMeMYnAme 是一样的。确保您在 db 和 request 中的所有名称都是小写的。
【解决方案2】:

首先,您的代码永远不会抛出异常说明用户名已被使用,因为您当前的规则提供对数据库的完全读写访问权限。

{
  "rules": {
    ".read": "true",  
    ".write": "true",
  }
}

您应该存储用户的 ID,而不是简单地在每个用户名下存储 "Saved"。当您想要删除用户帐户或更改其用户名时,这将有所帮助。然后你需要secure your RTDB with security rules。这两个步骤都包含在this answer 中。

"/usernames": {
  "bob": "userId1",
  "frank": "userId2",
  "c00lguy": "userId3"
}

为了让您仍然可以开发代码,同时允许您随时收紧代码,您可以命名特定的树以锁定:

{
  "rules": {
    // only signed in users can read user data
    // only the related user can edit it
    "users": {
      "$uid": {
        ".read": "auth != null",
        ".write": "auth != null && auth.uid == $uid"
      },
    },

    // all other trees are globally read/write accessible
    // TODO: remove this rule
    "$other": {
      ".read": "true",
      ".write": "true"
    }
  }
}

因此,要将/usernames 的限制与您当前的数据库规则合并,您可以使用:

{
  "rules": {
    "usernames": {
      "$username": {
        // world readable
        ".read": true,

        // must be logged in to edit, you can only claim free usernames OR delete owned usernames
        ".write": "auth != null && (!newData.exists() || auth.uid == newData.val()) && (!data.exists() || data.val() == auth.uid)",

        // strings only
        ".validate": "newData.isString()",
      }
    },

    // all other trees are globally read/write accessible
    // TODO: remove this rule
    "$other": {
      ".read": "true",
      ".write": "true"
    }
  }
}

现在您已经保护了数据库,您可以照常声明用户名,如果您尝试覆盖以前声明的用户名,它将引发权限错误。

重要的是,由于您使用的是addOnCompleteListener(),因此您需要先检查任务是成功还是失败,因为无论哪种情况都会调用您的事件处理程序的onComplete() 方法。这也消除了使用addOnFailureListener() 的需要。

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseDatabase.getInstance()
    .getReference("Usernames")
    .child(us.getText().toString()) // <- the desired username
    .setValue(uid) // <- who is claiming it
    .addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {
            if (task.isSuccessful()) {
                Toast.makeText(getApplicationContext(), "You have claimed this username.", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getApplicationContext(), "That username was taken.", Toast.LENGTH_SHORT).show();
            }
        }
    });

再现linked answer's 实用方法:


public Task<Boolean> isUsernameTaken(String username) {
    return FirebaseDatabase.getInstance()
        .getReference("Usernames")
        .child(username)
        .get() // <- forces getting value from server
        .onSuccessTask(snapshot -> Tasks.forResult(snapshot.exists));
}

public Task<Void> claimUsername(String username) {
    FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
    if (user == null) {
        throw new NullPointerException("User must be signed in first.");
    }
    return FirebaseDatabase.getInstance()
        .getReference("Usernames")
        .child(username) // <- the desired username
        .setValue(user.getUid()) // <- who is claiming it
}

【讨论】:

  • 我现在应该将所有代码都放在 fAuth.createUserWithEmailAndPassword 中吗?这是因为否则我无法获得 uid。还是我错了?
  • @pophome 用户成功通过身份验证后,您现在可以在代码中的任何位置使用FirebaseAuth.getInstance().getCurrentUser() 来获取当前登录的用户。您可以在createUserWithEmailAndPassword 链中添加第一个代码块或使用claimUsername(us.getText().toString()).addOnCompleteListener(...)。由你决定。我个人更喜欢使用函数来更好地理解代码。
  • 您建议的规则我应该如何插入它们?我还需要让读写保持真实吗?我已使用当前规则更新了我的应用程序
  • @pophome 我已经用与您当前规则相关的信息更新了答案
  • 我用粘贴规则时出现的错误更新了问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多