Form Validation 是在将数据发送到服务器之前检查表单所有字段正确性的过程。与单个字段的验证不同,Form Validation 考虑了字段之间的相互关系:密码确认、一个字段对另一个字段的依赖、条件必填。根据 Google Developers, 2026 的数据,Form Validation 应在提交时检查整个表单,并向用户提供所有错误的摘要。正确的表单验证可将注册转化率提高 25-35%,并减少输入错误的数量。
要点
Form Validation 是一个确保用户在表单中输入的所有数据在发送到服务器之前满足业务要求的过程。表单验证包括单独检查每个字段,以及交叉检查:密码是否与确认匹配、是否至少选中一个复选框、是否填写了所有必填字段、日期是否正确(例如,出生日期不在未来)。
与简单字段验证的区别在于,Form Validation 将表单作为一个整体来处理。如果条件字段未填写,它可以阻止提交,或在对话框中显示错误摘要。在复杂表单中(注册、下单、问卷),表单验证是一个独立的逻辑层,独立于 UI 进行测试。
根据 NN Group 的 UX 研究,如果用户在提交后立即看到错误,而不是在逐个字段后看到,用户完成表单填写的频率会高出 3 倍。然而,最佳结果来自组合:简单字段的即时验证(长度、格式)+ 提交时对交叉字段和业务逻辑的全面检查。
字段验证回答的问题是:这个特定字段的输入是否正确?电子邮件格式为 user@domain.com,电话由数字组成,密码长度超过 6 个字符。字段验证是隔离的——不依赖于其他字段,可以实时执行。结果:特定字段的错误或无错误。
表单验证回答的问题是:是否可以整体提交表单?它不仅考虑每个字段,还考虑它们的组合:密码和确认必须匹配,开始日期不能晚于结束日期,字段总和必须为 100%。表单验证在提交时执行,并返回总体结果:表单有效或无效。
在架构上,字段验证位于 UI 层(Fragment、ViewModel),而表单验证位于 领域层(use case、interactor)。这允许在不同的 UI 组件中重用表单验证,并在没有模拟器的情况下进行测试。在 Clean Architecture 中,表单验证是业务规则,而不是 UI 逻辑。
| 标准 | 字段验证 | 表单验证 |
|---|---|---|
| 检查对象 | 单个字段 | 所有字段 + 它们的相互关系 |
| 执行时机 | 实时 / 失去焦点时 | 提交表单时 |
| 结果 | 特定字段的错误 | 表单的总体状态 + 错误列表 |
| 架构层 | UI 层 | 领域层 |
Form Validation 有两种主要方法。第一种——命令式:开发人员编写一个函数,按顺序检查每个字段并收集错误列表。这种方法易于理解,但代码会随着每个新字段而增长。对于有 5 个字段的表单,命令式方法还算方便,对于 15 个字段——就有问题了。
第二种方法——声明式:验证规则通过注解或配置描述。库自己遍历所有字段,应用规则并返回结果。例如:针对 emailData 字段的 @Email 注解,针对确认字段的 @ConfirmPassword。声明式方法将验证代码缩短 3-5 倍,并使其易于阅读。
第三种方法——响应式,使用 RxJava 或 Kotlin Flow。每个字段表示为 Observable 或 StateFlow。表单验证订阅所有字段的变化,并在每次变化时重新计算整体状态。当所有字段都有效时,提交按钮自动变为活动状态。这种方法需要理解响应式编程,但提供最流畅的 UX。
考虑一个包含三个字段的注册表单:电子邮件、密码和密码确认。表单验证包括:通过 Patterns.EMAIL_ADDRESS 检查电子邮件,检查密码的最小长度 8 个字符和包含数字,检查密码和确认是否匹配。只有当所有三项检查都通过时,才能提交表单。
data class RegistrationForm(
val email: String,
val password: String,
val confirmPassword: String
)
fun validateRegistration(form: RegistrationForm): ValidationResult {
if (!Patterns.EMAIL_ADDRESS.matcher(form.email).matches())
return ValidationResult(false, "Invalid email address")
if (form.password.length < 8)
return ValidationResult(false, "Password too short")
if (form.password != form.confirmPassword)
return ValidationResult(false, "Passwords do not match")
return ValidationResult(true)
}
在示例中,validateRegistration 接受表单的 data class 并返回 ValidationResult。如果至少一项检查未通过,则返回 false 并附带相应消息。提交按钮的管理基于 Result:如果 isValid = true,按钮处于活动状态。为了实时更新状态,可以使用 LiveData<ValidationResult>,并在任何字段发生变化时更新按钮。
使用 Kotlin Flow 的响应式方法允许自动重新计算表单状态。每个字段表示为 MutableStateFlow<String>,combine 将它们合并为一个 Flow<ValidationResult>。UI 中的订阅更新提交按钮,无需手动调用验证。Google 推荐这种模式用于 Jetpack Compose 和 MVVM 架构。
Android Saripaar —— 最流行的 Android 验证库。允许直接注解字段和 View:@Email、@NotEmpty、@Password(min = 8, scheme = Password.Scheme.ALPHA_NUMERIC)。通过一行 validator.validate() 代码调用验证,带有回调。Saripaar 通过 EditText 上的 setError 自动设置错误。该库还支持针对特定业务规则的自定义注解。
RxBinding + RxJava —— 无需单独验证库的响应式方法。每个字段通过 RxTextView.textChanges() 发布更改。combineLatest 运算符组合所有字段并计算总体状态。优点:对验证管道完全控制,可以添加 debounce、throttle、filter。缺点:需要了解 RxJava。
Material Design Components —— 对 TextInputLayout 和 TextInputEditText 的内置支持。该库本身不提供验证功能,但提供了显示错误的 UI:setError()、setHelperText()、setCounterEnabled()。验证本身仍需要手动逻辑或 Saripaar。Material Components 负责显示,而不是检查。
第一个错误——仅在客户端验证。客户端的 Form Validation 旨在提升用户体验,而非安全性。攻击者可以直接向 API 发送请求,绕过验证。服务器必须重新检查所有字段。客户端验证不应是唯一的保护——它是为用户便利而增加的额外层,而非数据安全层。
第二个错误——阻止提交按钮但不显示消息。如果按钮处于非活动状态,用户应该看到哪些字段需要修正。没有解释的灰色按钮——表单转化率低的最常见原因之一。即使按钮被阻止,也要始终在字段旁边显示错误。用户必须理解到底是什么阻止了提交。
第三个错误——忽略交叉字段。单独验证每个字段是不够的。字段可能相互依赖:密码和确认、开始日期和结束日期、国家和城市。Form Validation 必须检查这些相互关系。仅检查单个字段会产生虚假的安全感——表单可能发送不一致的数据。
| 错误 | 后果 | 解决方案 |
|---|---|---|
| 仅客户端验证 | 安全漏洞 | 强制服务器检查 |
| 按钮无消息 | 表单转化率低 | 显示字段错误 |
| 无交叉检查 | 数据不一致 | 验证字段相互关系 |
| 检查过于频繁 | 引起用户反感 | 防抖和失去焦点时检查 |
常见问题
字段验证根据格式或长度检查单个值。Form Validation 同时检查所有字段,包括交叉检查:密码匹配、字段之间的相互依赖。字段验证在 UI 层执行,Form Validation 在领域层作为业务规则执行。
使用响应式方法:将所有字段合并为一个 Flow 或 Observable 并订阅更改。每当任何字段发生变化时,重新计算表单的总体状态。如果状态有效——按钮处于活动状态。使用 Kotlin Flow 配合 combine 或 RxJava 配合 combineLatest 进行自动更新。
Android Saripaar —— 使用注解进行声明式验证的最佳选择。如果项目使用 RxJava —— RxBinding 无需单独的库即可提供响应式方法。对于简单的表单,使用 Patterns 和 TextUtils 进行手动验证,无需外部依赖即可。
必须。客户端验证改善了用户体验,但不能保证安全性。服务器必须重新检查所有数据,因为 API 是直接可访问的。永远不要仅依赖客户端验证来防御不正确或恶意的数据。
在 Jetpack Compose 中,使用 Kotlin Flow 或 StateFlow 存储每个字段的状态。验证函数接受表单状态并返回 ValidationResult。提交按钮订阅总体状态。使用 OutlinedTextField 或 TextField Compose 中的 isError 显示错误。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。