Firebase Java Android 检查 child 是否未创建,因为它与同一个 parent 中已经存在的另一个相同

Firebase Java Android Check if a child was not created because it was the same as another already present in the same parent

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

但是,我希望用户名是唯一的,这样如果用户写下 parent 用户名中已经存在的用户名,他就会认识到还有另一个 child 是相同的。由于 firebase 不允许您创建具有相同名称的 children,我尝试这样做:

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,即使 child 与另一个同名,因此用户输入了一个已经输入的用户名。

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

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

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

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

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

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

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

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

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

不是简单地在每个用户名下存储 "Saved",您应该存储用户的 ID。这将在您以后想要删除用户帐户或更改用户名时提供帮助。那么你需要secure your RTDB with security rules. Both of these steps are covered in .

"/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(), you need to check if the task has succeeded or failed first because your event handler's onComplete() method will be called in either case. This also removes the need for using 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();
            }
        }
    });

重现 实用方法:


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
}