替换 firebase 密钥中无效字符的好方法?
Good way to replace invalid characters in firebase keys?
我的用例是保存用户信息。当我尝试使用用户的电子邮件地址作为密钥将数据保存到 Firebase 时,Firebase 抛出以下错误:
Error: Invalid key e@e.ee (cannot contain .$[]#
)
所以,显然,我无法通过他们的电子邮件索引用户信息。替换 .
的最佳做法是什么?
我已成功将 .
更改为 -
,但这并不能解决问题,因为某些电子邮件的地址中包含 -
。
目前,我正在使用
var cleanEmail = email.replace('.','`');
但是这可能会导致冲突。
我们已经多次处理过这个问题,虽然表面上看起来使用电子邮件作为密钥是一个简单的解决方案,但它会导致很多其他问题:必须 clean/parse电子邮件,以便实际使用。如果电子邮件更改了怎么办?
我们发现更改数据存储方式的格式是更好的途径。假设你只需要存储一件事,用户名。
john@somecompany.com: "John Smith"
改为
randomly_generated_node_name
email: "john@somecompany.com"
first: "John"
last: "Smith"
randomly_generated_node_name 是 Firebase 可以通过 childByAutoId 生成的字符串,或者实际上是不直接绑定到数据的任何类型的引用。
这提供了很大的灵活性:您现在可以更改人员的姓氏 - 比如他们是否结婚。或者更改他们的电子邮件。您可以添加可用于排序的 'index' 子项 0、1、2 等。可以查询任何子数据的数据。都是因为 randomly_generated_node_name 是对节点内变量子数据的静态引用。
它还允许您在将来扩展数据而不更改现有数据。添加地址、最喜欢的食物、排序索引等
编辑:ObjC 中电子邮件的 Firebase 查询:
//references all of the users ordered by email
FQuery *allUsers = [myUsersRef queryOrderedByChild:@"email"];
//ref the user with this email
FQuery *thisSpecificUser = [allUsers queryEqualToValue:@“john@somecompany.com”];
//load the user with this email
[thisSpecificUser observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
//do something with this user
}];
在电子邮件地址中,将点 .
替换为逗号 ,
。此模式是最佳实践。
逗号 ,
不是 电子邮件地址中允许的字符,但 是 Firebase 密钥中允许的字符。对称地,点.
是电子邮件地址中允许的字符,但不是在Firebase密钥中允许的字符。所以直接替换将解决你的问题。您可以在不循环的情况下为电子邮件地址编制索引。
您还有一个问题。
const cleanEmail = email.replace('.',','); // only replaces first dot
只会替换第一个点 .
但是电子邮件地址可以有多个点。要替换所有点,请使用 正则表达式 .
const cleanEmail = email.replace(/\./g, ','); // replaces all dots
或者,您也可以使用 split()
- join()
模式来替换所有点。
const cleanEmail = email.split('.').join(','); // also replaces all dots
我可以想到两种主要的方法来解决这个问题:
- Encode/Decode函数
由于 Firebase 密钥中允许的字符集有限,解决方案是将密钥转换为有效格式 (encode)。然后有一个反函数(解码)将编码的密钥转换回原始密钥。
一般的 encode/decode 函数可能会将原始密钥转换为字节,然后将它们转换为十六进制表示形式。但是密钥的大小可能是个问题。
假设您要使用电子邮件作为键来存储用户:
# path: /users/{email} is User;
/users/alice@email.com: {
name: "Alice",
email: "alice@email.com"
}
由于路径中的点,上述示例无法运行。所以我们使用 encode 函数将密钥转换为有效格式。 alice@email.com
十六进制为616c69636540656d61696c2e636f6d
,则:
# path: /users/{hex(email)} is User;
/users/616c69636540656d61696c2e636f6d: {
name: "Alice",
email: "alice@email.com"
}
只要共享相同的 hex
功能,任何客户端都可以访问该资源。
编辑:Base64 也可用于 encode/decode 密钥。可能比十六进制更有效,但有许多不同的实现。如果客户端不共享完全相同的实现,那么它们将无法正常工作。
也可以使用专门的功能(例如只处理电子邮件)。但一定要处理所有边缘情况。
- 使用存储的原始密钥编码函数
对密钥进行单向转换要容易得多。因此,不使用解码功能,只需将原始密钥存储在数据库中即可。
一个很好的 encode 函数是 SHA-256 算法。这是一种在许多平台上都有实现的常见算法。而且碰撞的几率很小。
之前使用 SHA-256 的例子变成了这样:
# path: /users/{sha256(email)} is User;
/users/55bf4952e2308638427d0c28891b31b8cd3a88d1610b81f0a605da25fd9c351a: {
name: "Alice",
email: "alice@email.com"
}
任何具有原始密钥(电子邮件)的客户端都可以找到此条目,因为编码功能是已知的(它是已知的)。而且,即使密钥变大,SHA-256 的大小也将始终相同,因此,保证是有效的 Firebase 密钥。
我使用以下代码将电子邮件转换为散列,然后将散列用作 firebase 中的键
public class HashingUtils {
public HashingUtils() {
}
//generate 256 bits hash using SHA-256
public String generateHashkeySHA_256(String email){
String result = null;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(email.getBytes("UTF-8"));
return byteToHex(hash); // make it printable
}catch(Exception ex) {
ex.printStackTrace();
}
return result;
}
//generate 160bits hash using SHA-1
public String generateHashkeySHA_1(String email){
String result = null;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(email.getBytes("UTF-8"));
return byteToHex(hash); // make it printable
}catch(Exception ex) {
ex.printStackTrace();
}
return result;
}
public String byteToHex(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
String hex = formatter.toString();
return hex;
}
}
将用户添加到 firebase 的代码
public void addUser(User user) {
Log.d(TAG, "addUser: ");
DatabaseReference userRef= database.getReference("User");
if(!TextUtils.isEmpty(user.getEmailId())){
String hashEmailId= hashingUtils.generateHashkeySHA_256(user.getEmailId());
Log.d(TAG, "addUser: hashEmailId"+hashEmailId);
userRef.child(hashEmailId).setValue(user);
}
else {
Log.d(TAG,"addUser: empty emailId");
}
}
我的用例是保存用户信息。当我尝试使用用户的电子邮件地址作为密钥将数据保存到 Firebase 时,Firebase 抛出以下错误:
Error: Invalid key e@e.ee (cannot contain
.$[]#
)
所以,显然,我无法通过他们的电子邮件索引用户信息。替换 .
的最佳做法是什么?
我已成功将 .
更改为 -
,但这并不能解决问题,因为某些电子邮件的地址中包含 -
。
目前,我正在使用
var cleanEmail = email.replace('.','`');
但是这可能会导致冲突。
我们已经多次处理过这个问题,虽然表面上看起来使用电子邮件作为密钥是一个简单的解决方案,但它会导致很多其他问题:必须 clean/parse电子邮件,以便实际使用。如果电子邮件更改了怎么办?
我们发现更改数据存储方式的格式是更好的途径。假设你只需要存储一件事,用户名。
john@somecompany.com: "John Smith"
改为
randomly_generated_node_name
email: "john@somecompany.com"
first: "John"
last: "Smith"
randomly_generated_node_name 是 Firebase 可以通过 childByAutoId 生成的字符串,或者实际上是不直接绑定到数据的任何类型的引用。
这提供了很大的灵活性:您现在可以更改人员的姓氏 - 比如他们是否结婚。或者更改他们的电子邮件。您可以添加可用于排序的 'index' 子项 0、1、2 等。可以查询任何子数据的数据。都是因为 randomly_generated_node_name 是对节点内变量子数据的静态引用。
它还允许您在将来扩展数据而不更改现有数据。添加地址、最喜欢的食物、排序索引等
编辑:ObjC 中电子邮件的 Firebase 查询:
//references all of the users ordered by email
FQuery *allUsers = [myUsersRef queryOrderedByChild:@"email"];
//ref the user with this email
FQuery *thisSpecificUser = [allUsers queryEqualToValue:@“john@somecompany.com”];
//load the user with this email
[thisSpecificUser observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
//do something with this user
}];
在电子邮件地址中,将点 .
替换为逗号 ,
。此模式是最佳实践。
逗号 ,
不是 电子邮件地址中允许的字符,但 是 Firebase 密钥中允许的字符。对称地,点.
是电子邮件地址中允许的字符,但不是在Firebase密钥中允许的字符。所以直接替换将解决你的问题。您可以在不循环的情况下为电子邮件地址编制索引。
您还有一个问题。
const cleanEmail = email.replace('.',','); // only replaces first dot
只会替换第一个点 .
但是电子邮件地址可以有多个点。要替换所有点,请使用 正则表达式 .
const cleanEmail = email.replace(/\./g, ','); // replaces all dots
或者,您也可以使用 split()
- join()
模式来替换所有点。
const cleanEmail = email.split('.').join(','); // also replaces all dots
我可以想到两种主要的方法来解决这个问题:
- Encode/Decode函数
由于 Firebase 密钥中允许的字符集有限,解决方案是将密钥转换为有效格式 (encode)。然后有一个反函数(解码)将编码的密钥转换回原始密钥。
一般的 encode/decode 函数可能会将原始密钥转换为字节,然后将它们转换为十六进制表示形式。但是密钥的大小可能是个问题。
假设您要使用电子邮件作为键来存储用户:
# path: /users/{email} is User;
/users/alice@email.com: {
name: "Alice",
email: "alice@email.com"
}
由于路径中的点,上述示例无法运行。所以我们使用 encode 函数将密钥转换为有效格式。 alice@email.com
十六进制为616c69636540656d61696c2e636f6d
,则:
# path: /users/{hex(email)} is User;
/users/616c69636540656d61696c2e636f6d: {
name: "Alice",
email: "alice@email.com"
}
只要共享相同的 hex
功能,任何客户端都可以访问该资源。
编辑:Base64 也可用于 encode/decode 密钥。可能比十六进制更有效,但有许多不同的实现。如果客户端不共享完全相同的实现,那么它们将无法正常工作。
也可以使用专门的功能(例如只处理电子邮件)。但一定要处理所有边缘情况。
- 使用存储的原始密钥编码函数
对密钥进行单向转换要容易得多。因此,不使用解码功能,只需将原始密钥存储在数据库中即可。
一个很好的 encode 函数是 SHA-256 算法。这是一种在许多平台上都有实现的常见算法。而且碰撞的几率很小。
之前使用 SHA-256 的例子变成了这样:
# path: /users/{sha256(email)} is User;
/users/55bf4952e2308638427d0c28891b31b8cd3a88d1610b81f0a605da25fd9c351a: {
name: "Alice",
email: "alice@email.com"
}
任何具有原始密钥(电子邮件)的客户端都可以找到此条目,因为编码功能是已知的(它是已知的)。而且,即使密钥变大,SHA-256 的大小也将始终相同,因此,保证是有效的 Firebase 密钥。
我使用以下代码将电子邮件转换为散列,然后将散列用作 firebase 中的键
public class HashingUtils {
public HashingUtils() {
}
//generate 256 bits hash using SHA-256
public String generateHashkeySHA_256(String email){
String result = null;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(email.getBytes("UTF-8"));
return byteToHex(hash); // make it printable
}catch(Exception ex) {
ex.printStackTrace();
}
return result;
}
//generate 160bits hash using SHA-1
public String generateHashkeySHA_1(String email){
String result = null;
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(email.getBytes("UTF-8"));
return byteToHex(hash); // make it printable
}catch(Exception ex) {
ex.printStackTrace();
}
return result;
}
public String byteToHex(byte[] bytes) {
Formatter formatter = new Formatter();
for (byte b : bytes) {
formatter.format("%02x", b);
}
String hex = formatter.toString();
return hex;
}
}
将用户添加到 firebase 的代码
public void addUser(User user) {
Log.d(TAG, "addUser: ");
DatabaseReference userRef= database.getReference("User");
if(!TextUtils.isEmpty(user.getEmailId())){
String hashEmailId= hashingUtils.generateHashkeySHA_256(user.getEmailId());
Log.d(TAG, "addUser: hashEmailId"+hashEmailId);
userRef.child(hashEmailId).setValue(user);
}
else {
Log.d(TAG,"addUser: empty emailId");
}
}