Swift中Firebase hasChild遇InvalidPathValidation,需支持含点用户名检测
解决Firebase检测含点(.)用户名占用的问题
这个问题我之前也碰到过,Firebase Realtime Database的路径规则确实会限制hasChild()里的参数不能包含.、#这些特殊字符——因为它会把参数当作路径节点名来解析,自然就触发了InvalidPathValidation异常。要支持含点的用户名,我们可以用两种可行的方案来实现:
方案一:转义特殊字符后存储与检测
核心思路是把用户名里的Firebase路径禁止字符替换成合法字符,存储和查询时统一处理,这样就能正常使用hasChild()了。
比如我们可以把.替换成__DOT__(你也可以选其他不会和用户名冲突的标识),先写个工具方法统一处理:
// 给String扩展转义/反转义方法 extension String { func escapedForFirebaseKey() -> String { return self.replacingOccurrences(of: ".", with: "__DOT__") .replacingOccurrences(of: "#", with: "__HASH__") .replacingOccurrences(of: "$", with: "__DOLLAR__") .replacingOccurrences(of: "[", with: "__BRACKET_OPEN__") .replacingOccurrences(of: "]", with: "__BRACKET_CLOSE__") } func unescapedFromFirebaseKey() -> String { return self.replacingOccurrences(of: "__DOT__", with: ".") .replacingOccurrences(of: "__HASH__", with: "#") .replacingOccurrences(of: "__DOLLAR__", with: "$") .replacingOccurrences(of: "__BRACKET_OPEN__", with: "[") .replacingOccurrences(of: "__BRACKET_CLOSE__", with: "]") } } // 检测用户名是否被占用的业务代码 let username = "username.test" let escapedUsername = username.escapedForFirebaseKey() let ref = Api.User.REF_USERNAME_LIST ref.observeSingleEvent(of: .value, with: { (snapshot) in if snapshot.hasChild(escapedUsername) { print("taken") return } else { print("good") // 存储用户名时,同样要存转义后的版本 ref.child(escapedUsername).setValue(true) } }) { (error) in print(error.localizedDescription) }
后续如果需要从Firebase获取原始用户名,调用unescapedFromFirebaseKey()反转义即可。这种方式查询效率很高,直接通过节点键判断,适合对性能要求高的场景。
方案二:使用Firebase查询替代hasChild
如果不想做字符转义,也可以调整数据结构,用查询来检测用户名是否存在。需要把用户名作为节点的字段(比如username字段),而不是节点键:
首先调整Firebase的数据结构,比如username-list下的节点格式如下:
username-list: { "user-xxxx1": { "username": "username.test" }, "user-xxxx2": { "username": "another.user" } }
然后用queryOrdered(byChild:)和queryEqual(toValue:)来查询:
let username = "username.test" let ref = Api.User.REF_USERNAME_LIST // 查询所有username字段匹配目标值的节点 ref.queryOrdered(byChild: "username") .queryEqual(toValue: username) .observeSingleEvent(of: .value) { snapshot in if !snapshot.exists() { print("good") } else { print("taken") } } withCancel: { error in print(error.localizedDescription) }
注意:使用这种方案时,必须在Firebase控制台给username-list节点添加索引规则,否则查询会很慢甚至报错:
{ "rules": { "username-list": { ".indexOn": "username" } } }
这种方案不需要处理字符转义,数据结构更直观,但查询性能略低于方案一。
你可以根据自己的业务需求选择合适的方案~
内容的提问来源于stack exchange,提问作者KevinB
相关产品推荐
相关产品推荐

