数据验证工具 | 基于鸿蒙KMP
本文介绍了一个基于Kotlin Multiplatform(KMP)和OpenHarmony平台的数据验证库,支持跨平台使用。该库提供了邮箱、电话、密码、URL等多种数据验证功能,以及自定义验证规则和批量验证能力。通过KMP技术,开发者只需编写一次Kotlin代码,即可在JVM、JS和OpenHarmony/ArkTS等多个平台上复用。核心验证类包含各种验证方法的实现,如格式检查、长度验证、范围验

项目概述
数据验证是现代应用开发中的关键功能。无论是在用户输入验证、API 请求验证、数据完整性检查还是业务规则验证中,都需要进行数据的验证和检查。然而,不同的编程语言和平台对数据验证的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的数据验证库示例。这个库提供了一套完整的数据验证能力,包括字段验证、规则验证、自定义验证等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用
核心功能模块
- 邮箱验证: 验证邮箱格式
- 电话验证: 验证电话号码格式
- 密码验证: 验证密码强度
- URL 验证: 验证 URL 格式
- 数字验证: 验证数字范围
- 字符串验证: 验证字符串长度和格式
- 自定义验证: 支持自定义验证规则
- 批量验证: 批量验证多个字段
Kotlin 实现
核心验证类
// 文件: src/commonMain/kotlin/Validator.kt
/**
* 数据验证工具类
* 提供各种数据验证功能
*/
class Validator {
data class ValidationResult(
val isValid: Boolean,
val errors: List<String> = emptyList(),
val warnings: List<String> = emptyList()
)
data class ValidationRule(
val name: String,
val pattern: String? = null,
val minLength: Int? = null,
val maxLength: Int? = null,
val required: Boolean = true
)
/**
* 验证邮箱格式
* @param email 邮箱地址
* @return 验证结果
*/
fun validateEmail(email: String): ValidationResult {
val errors = mutableListOf<String>()
if (email.isEmpty()) {
errors.add("邮箱不能为空")
return ValidationResult(false, errors)
}
val emailPattern = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$"
if (!email.matches(Regex(emailPattern))) {
errors.add("邮箱格式不正确")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证电话号码
* @param phone 电话号码
* @return 验证结果
*/
fun validatePhone(phone: String): ValidationResult {
val errors = mutableListOf<String>()
if (phone.isEmpty()) {
errors.add("电话号码不能为空")
return ValidationResult(false, errors)
}
val phonePattern = "^1[3-9]\\d{9}$"
if (!phone.matches(Regex(phonePattern))) {
errors.add("电话号码格式不正确")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证密码强度
* @param password 密码
* @return 验证结果
*/
fun validatePassword(password: String): ValidationResult {
val errors = mutableListOf<String>()
val warnings = mutableListOf<String>()
if (password.isEmpty()) {
errors.add("密码不能为空")
return ValidationResult(false, errors)
}
if (password.length < 8) {
errors.add("密码长度至少 8 个字符")
}
if (!password.any { it.isUpperCase() }) {
warnings.add("建议包含大写字母")
}
if (!password.any { it.isLowerCase() }) {
warnings.add("建议包含小写字母")
}
if (!password.any { it.isDigit() }) {
warnings.add("建议包含数字")
}
if (!password.any { !it.isLetterOrDigit() }) {
warnings.add("建议包含特殊字符")
}
return ValidationResult(errors.isEmpty(), errors, warnings)
}
/**
* 验证 URL 格式
* @param url URL 地址
* @return 验证结果
*/
fun validateUrl(url: String): ValidationResult {
val errors = mutableListOf<String>()
if (url.isEmpty()) {
errors.add("URL 不能为空")
return ValidationResult(false, errors)
}
val urlPattern = "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$"
if (!url.matches(Regex(urlPattern, RegexOption.IGNORE_CASE))) {
errors.add("URL 格式不正确")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证数字范围
* @param value 数值
* @param min 最小值
* @param max 最大值
* @return 验证结果
*/
fun validateNumberRange(value: Double, min: Double, max: Double): ValidationResult {
val errors = mutableListOf<String>()
if (value < min) {
errors.add("数值不能小于 $min")
}
if (value > max) {
errors.add("数值不能大于 $max")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证字符串长度
* @param text 文本
* @param minLength 最小长度
* @param maxLength 最大长度
* @return 验证结果
*/
fun validateStringLength(text: String, minLength: Int, maxLength: Int): ValidationResult {
val errors = mutableListOf<String>()
if (text.length < minLength) {
errors.add("文本长度至少 $minLength 个字符")
}
if (text.length > maxLength) {
errors.add("文本长度不能超过 $maxLength 个字符")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证是否为整数
* @param value 值
* @return 验证结果
*/
fun validateInteger(value: String): ValidationResult {
val errors = mutableListOf<String>()
if (!value.matches(Regex("^-?\\d+$"))) {
errors.add("必须是整数")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 验证是否为浮点数
* @param value 值
* @return 验证结果
*/
fun validateFloat(value: String): ValidationResult {
val errors = mutableListOf<String>()
if (!value.matches(Regex("^-?\\d+(\\.\\d+)?$"))) {
errors.add("必须是数字")
}
return ValidationResult(errors.isEmpty(), errors)
}
/**
* 批量验证字段
* @param fields 字段映射
* @param rules 验证规则
* @return 验证结果
*/
fun validateFields(fields: Map<String, String>, rules: Map<String, ValidationRule>): ValidationResult {
val allErrors = mutableListOf<String>()
for ((fieldName, rule) in rules) {
val value = fields[fieldName] ?: ""
if (rule.required && value.isEmpty()) {
allErrors.add("$fieldName 不能为空")
continue
}
if (rule.minLength != null && value.length < rule.minLength) {
allErrors.add("$fieldName 长度至少 ${rule.minLength} 个字符")
}
if (rule.maxLength != null && value.length > rule.maxLength) {
allErrors.add("$fieldName 长度不能超过 ${rule.maxLength} 个字符")
}
if (rule.pattern != null && !value.matches(Regex(rule.pattern))) {
allErrors.add("$fieldName 格式不正确")
}
}
return ValidationResult(allErrors.isEmpty(), allErrors)
}
/**
* 生成验证报告
* @param result 验证结果
* @return 报告字符串
*/
fun generateValidationReport(result: ValidationResult): String {
val report = StringBuilder()
report.append("数据验证报告\n")
report.append("=".repeat(40)).append("\n")
report.append("验证状态: ${if (result.isValid) "通过" else "失败"}\n")
report.append("错误数: ${result.errors.size}\n")
report.append("警告数: ${result.warnings.size}\n")
if (result.errors.isNotEmpty()) {
report.append("\n错误:\n")
result.errors.forEach { report.append("- $it\n") }
}
if (result.warnings.isNotEmpty()) {
report.append("\n警告:\n")
result.warnings.forEach { report.append("- $it\n") }
}
return report.toString()
}
}
Kotlin 实现的核心特点
Kotlin 实现中的验证功能充分利用了 Kotlin 标准库的正则表达式和字符串处理能力。邮箱验证使用了正则表达式匹配。密码验证使用了字符检查。
字符串验证使用了长度检查。批量验证使用了循环和映射。报告生成使用了字符串构建器。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)
/**
* Validator 类的 JavaScript 版本
* 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
*/
class Validator {
/**
* 验证邮箱格式
* @param {string} email - 邮箱地址
* @returns {Object} 验证结果
*/
validateEmail(email) {
const errors = [];
if (email.length === 0) {
errors.push('邮箱不能为空');
return { isValid: false, errors: errors, warnings: [] };
}
const emailPattern = /^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/;
if (!emailPattern.test(email)) {
errors.push('邮箱格式不正确');
}
return { isValid: errors.length === 0, errors: errors, warnings: [] };
}
/**
* 验证电话号码
* @param {string} phone - 电话号码
* @returns {Object} 验证结果
*/
validatePhone(phone) {
const errors = [];
if (phone.length === 0) {
errors.push('电话号码不能为空');
return { isValid: false, errors: errors, warnings: [] };
}
const phonePattern = /^1[3-9]\d{9}$/;
if (!phonePattern.test(phone)) {
errors.push('电话号码格式不正确');
}
return { isValid: errors.length === 0, errors: errors, warnings: [] };
}
/**
* 验证密码强度
* @param {string} password - 密码
* @returns {Object} 验证结果
*/
validatePassword(password) {
const errors = [];
const warnings = [];
if (password.length === 0) {
errors.push('密码不能为空');
return { isValid: false, errors: errors, warnings: warnings };
}
if (password.length < 8) {
errors.push('密码长度至少 8 个字符');
}
if (!/[A-Z]/.test(password)) {
warnings.push('建议包含大写字母');
}
if (!/[a-z]/.test(password)) {
warnings.push('建议包含小写字母');
}
if (!/\d/.test(password)) {
warnings.push('建议包含数字');
}
if (!/[^A-Za-z0-9]/.test(password)) {
warnings.push('建议包含特殊字符');
}
return { isValid: errors.length === 0, errors: errors, warnings: warnings };
}
/**
* 验证 URL 格式
* @param {string} url - URL 地址
* @returns {Object} 验证结果
*/
validateUrl(url) {
const errors = [];
if (url.length === 0) {
errors.push('URL 不能为空');
return { isValid: false, errors: errors, warnings: [] };
}
const urlPattern = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/i;
if (!urlPattern.test(url)) {
errors.push('URL 格式不正确');
}
return { isValid: errors.length === 0, errors: errors, warnings: [] };
}
/**
* 验证数字范围
* @param {number} value - 数值
* @param {number} min - 最小值
* @param {number} max - 最大值
* @returns {Object} 验证结果
*/
validateNumberRange(value, min, max) {
const errors = [];
if (value < min) {
errors.push(`数值不能小于 ${min}`);
}
if (value > max) {
errors.push(`数值不能大于 ${max}`);
}
return { isValid: errors.length === 0, errors: errors, warnings: [] };
}
/**
* 验证字符串长度
* @param {string} text - 文本
* @param {number} minLength - 最小长度
* @param {number} maxLength - 最大长度
* @returns {Object} 验证结果
*/
validateStringLength(text, minLength, maxLength) {
const errors = [];
if (text.length < minLength) {
errors.push(`文本长度至少 ${minLength} 个字符`);
}
if (text.length > maxLength) {
errors.push(`文本长度不能超过 ${maxLength} 个字符`);
}
return { isValid: errors.length === 0, errors: errors, warnings: [] };
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的正则表达式提供了验证能力。
test 方法用于正则匹配。push 方法用于错误收集。条件判断用于验证逻辑。
ArkTS 调用代码
OpenHarmony 应用集成
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/ValidatorPage.ets
import { Validator } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct ValidatorPage {
@State selectedValidation: string = 'email';
@State inputValue: string = '';
@State result: string = '';
@State resultTitle: string = '';
private validator = new Validator();
private validations = [
{ name: '📧 邮箱', value: 'email' },
{ name: '📱 电话', value: 'phone' },
{ name: '🔐 密码', value: 'password' },
{ name: '🔗 URL', value: 'url' },
{ name: '🔢 数字', value: 'number' },
{ name: '📝 字符串', value: 'string' },
{ name: '🔤 整数', value: 'integer' },
{ name: '📊 报告', value: 'report' }
];
build() {
Column() {
// 标题
Text('✓ 数据验证库示例')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(20)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
Scroll() {
Column() {
// 验证类型选择
Column() {
Text('选择验证类型')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.validations, (val: { name: string; value: string }) => {
Button(val.name)
.layoutWeight(1)
.height(40)
.margin({ right: 8, bottom: 8 })
.backgroundColor(this.selectedValidation === val.value ? '#1A237E' : '#E0E0E0')
.fontColor(this.selectedValidation === val.value ? '#FFFFFF' : '#333333')
.fontSize(11)
.onClick(() => {
this.selectedValidation = val.value;
this.result = '';
this.resultTitle = '';
})
})
}
.width('100%')
}
.width('95%')
.margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 输入值
Column() {
Text('输入要验证的值')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 8 })
TextInput({ placeholder: '输入验证数据', text: this.inputValue })
.onChange((value) => this.inputValue = value)
.width('100%')
.height(50)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 操作按钮
Row() {
Button('✨ 验证')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.borderRadius(6)
.onClick(() => this.executeValidation())
Blank()
.width(12)
Button('🔄 清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(14)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.inputValue = '';
this.result = '';
this.resultTitle = '';
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
// 结果显示
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(12)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(300)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private executeValidation() {
const value = this.inputValue || 'test@example.com';
try {
switch (this.selectedValidation) {
case 'email':
const emailResult = this.validator.validateEmail(value);
this.resultTitle = '📧 邮箱验证';
this.result = `输入: ${value}\n状态: ${emailResult.isValid ? '✓ 有效' : '✗ 无效'}\n${emailResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'phone':
const phoneResult = this.validator.validatePhone(value);
this.resultTitle = '📱 电话验证';
this.result = `输入: ${value}\n状态: ${phoneResult.isValid ? '✓ 有效' : '✗ 无效'}\n${phoneResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'password':
const passwordResult = this.validator.validatePassword(value);
this.resultTitle = '🔐 密码验证';
this.result = `输入: ${value}\n状态: ${passwordResult.isValid ? '✓ 有效' : '✗ 无效'}\n${passwordResult.errors.map(e => `错误: ${e}`).join('\n')}${passwordResult.warnings.length > 0 ? '\n' + passwordResult.warnings.map(w => `警告: ${w}`).join('\n') : ''}`;
break;
case 'url':
const urlResult = this.validator.validateUrl(value);
this.resultTitle = '🔗 URL 验证';
this.result = `输入: ${value}\n状态: ${urlResult.isValid ? '✓ 有效' : '✗ 无效'}\n${urlResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'number':
const numberResult = this.validator.validateNumberRange(parseFloat(value), 0, 100);
this.resultTitle = '🔢 数字验证';
this.result = `输入: ${value}\n范围: 0-100\n状态: ${numberResult.isValid ? '✓ 有效' : '✗ 无效'}\n${numberResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'string':
const stringResult = this.validator.validateStringLength(value, 3, 20);
this.resultTitle = '📝 字符串验证';
this.result = `输入: ${value}\n长度范围: 3-20\n状态: ${stringResult.isValid ? '✓ 有效' : '✗ 无效'}\n${stringResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'integer':
const intResult = this.validator.validateInteger(value);
this.resultTitle = '🔤 整数验证';
this.result = `输入: ${value}\n状态: ${intResult.isValid ? '✓ 有效' : '✗ 无效'}\n${intResult.errors.map(e => `错误: ${e}`).join('\n')}`;
break;
case 'report':
const testResult = this.validator.validateEmail(value);
const report = this.validator.generateValidationReport(testResult);
this.resultTitle = '📊 验证报告';
this.result = report;
break;
}
} catch (e) {
this.resultTitle = '❌ 验证出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 应用中集成验证工具库需要考虑多种验证类型和用户体验。我们设计了一个灵活的 UI,能够支持不同的数据验证操作。
验证类型选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。输入值使用了 TextInput 组件。
结果显示使用了可选择的文本,这样用户可以轻松复制验证结果。对于不同的验证类型,我们显示了相应的验证处理结果。
工作流程详解
数据验证的完整流程
- 验证类型选择: 用户在 ArkTS UI 中选择要执行的验证类型
- 数据输入: 用户输入要验证的数据
- 处理执行: 调用 Validator 的相应方法
- 结果展示: 将验证结果显示在 UI 中
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,数据验证的逻辑和结果都是完全相同的。
实际应用场景
用户注册
在用户注册时,需要验证邮箱、电话和密码。这个工具库提供了完整的注册数据验证功能。
表单提交
在提交表单时,需要验证各个字段。这个工具库提供了表单字段验证能力。
API 请求
在发送 API 请求时,需要验证请求数据。这个工具库提供了 API 数据验证功能。
数据导入
在导入数据时,需要验证数据格式和内容。这个工具库提供了数据导入验证能力。
性能优化
缓存验证规则
在频繁进行相同验证时,可以缓存验证规则以避免重复编译。
异步验证
在进行复杂验证时,应该考虑使用异步处理以避免阻塞 UI。
安全性考虑
输入清理
在验证用户输入时,应该进行清理以防止恶意输入。
验证规则保护
在定义验证规则时,应该避免暴露敏感信息。
总结
这个 KMP OpenHarmony 数据验证库示例展示了如何使用现代的跨平台技术来处理常见的数据验证任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
数据验证是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种数据验证操作,从而提高应用的数据质量和用户体验。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的验证规则、实现更复杂的验证逻辑等高级特性。同时,定期进行测试和优化,确保应用的验证系统保持高效运行。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)