5.4.2 校验表单

如果用户在提交表单的时候,username 或 password 文本域为空的话,那么将会导致在新建 Spitter 对象中,username 或 password 是空的 String。至少这是一种怪异的行为。如果这种现象不处理的话,这将会出现安全问题,因为不管是谁只要提交一个空的表单就能登录应用。

同时,我们还应该阻止用户提交空的 firstName 和 / 或 lastName,使应用仅在一定程度上保持匿名性。有个好的办法就是限制这些输入域值的长度,保持它们的值在一个合理的长度范围,避免这些输入域的误用。

有种处理校验的方式非常初级,那就是在 processRegistration() 方法中添加代码来检查值的合法性,如果值不合法的话,就将注册表单重新显示给用户。这是一个很简短的方法,因此,添加一些额外的 if 语句也不是什么大问题,对吧?

与其让校验逻辑弄乱我们的处理器方法,还不如使用 Spring 对 Java 校验 API(Java Validation API,又称 JSR-303)的支持。从 Spring 3.0 开 始,在 Spring MVC 中提供了对 Java 校验 API 的支持。在 Spring MVC 中要使用 Java 校验 API 的话,并不需要什么额外的配置。只要保证在类路径下包含这个 Java API 的实现即可,比如 Hibernate Validator。

Java 校验 API 定义了多个注解,这些注解可以放到属性上,从而限制这些属性的值。所有的注解都位于 javax.validation.constraints 包中。表 5.1 列出了这些校验注解。

注解

描述

@AssertFalse

所注解的元素必须是 Boolean 类型,并且值为 false

@AssertTrue

所注解的元素必须是 Boolean 类型,并且值为 true

@DecimalMax

所注解的元素必须是数字,并且它的值要小于或等于给定的 BigDecimalString 值

@DecimalMin

所注解的元素必须是数字,并且它的值要大于或等于给定的 BigDecimalString 值

@Digits

所注解的元素必须是数字,并且它的值必须有指定的位数

@Future

所注解的元素的值必须是一个将来的日期

@Max

所注解的元素必须是数字,并且它的值要小于或等于给定的值

@Min

所注解的元素必须是数字,并且它的值要大于或等于给定的值

@NotNull

所注解元素的值必须不能为 null

@Null

所注解元素的值必须为 null

@Past

所注解的元素的值必须是一个已过去的日期

@Pattern

所注解的元素的值必须匹配给定的正则表达式

@Size

所注解的元素的值必须是 String、集合或数组,并且它的长度要符合给定的范围

除了表 5.1中的注解,Java 校验 API 的实现可能还会提供额外的校验注解。同时,也可以定义自己的限制条件。但就我们来讲,将会关注于上表中的两个核心限制条件。

请考虑要添加到 Spitter 域上的限制条件,似乎需要使用 @NotNull 和 @Size 注解。我们所要做的事情就是将这些注解添加到 Spitter 的属性上。如下的程序清单展现了 Spitter 类,它的属性已经添加了校验注解。

package spittr;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class Spitter {

  private Long id;
  
  @NotNull
  @Size(min=5, max=16)
  private String username;

  @NotNull
  @Size(min=5, max=25)
  private String password;
  
  @NotNull
  @Size(min=2, max=30)
  private String firstName;

  @NotNull
  @Size(min=2, max=30)
  private String lastName;
  
  ...
}

现在,Spitter 的所有属性都添加了 @NotNull 注解,以确保它们的值不为 null。类似地,属性上也添加了 @Size 注解以限制它们的长度在最大值和最小值之间。对 Spittr 应用来说,这意味着用户必须要填完注册表单,并且值的长度要在给定的范围内。

我们已经为 Spitter 添加了校验注解,接下来需要修改 processRegistration() 方法来应用校验功能。启用校验功能的 processRegistration() 如下所示:

程序清单 5.19 processRegistration():确保所提交的数据是合法的
@RequestMapping(value="/register", method=POST)
public String processRegistration(
    @Valid Spitter spitter, 
    Errors errors) {
  if (errors.hasErrors()) {
    return "registerForm";
  }
    
  spitterRepository.save(spitter);
  return "redirect:/spitter/" + spitter.getUsername();
}

与程序清单5.17 中最初的 processRegistration() 方法相比,这里有了很大的变化。Spitter 参数添加了 @Valid 注解,这会告知 Spring,需要确保这个对象满足校验限制。

在 Spitter 属性上添加校验限制并不能阻止表单提交。即便用户没有填写某个域或者某个域所给定的值超出了最大长度,processRegistration() 方法依然会被调用。这样,我们就需要处理校验的错误,就像在 processRegistration() 方法中所看到的那样。

如果有校验出现错误的话,那么这些错误可以通过 Errors 对象进行访问,现在这个对象已作为 processRegistration() 方法的参数。(很重要一点需要注意,Errors 参数要紧跟在带有 @Valid 注解的参数后面,@Valid 注解所标注的就是要检验的参数。)processRegistration() 方法所做的第一件事就是调用Errors.hasErrors() 来检查是否有错误。

如果有错误的话,Errors.hasErrors() 将会返回到registerForm,也就是注册表单的视图。这能够让用户的浏览器重新回到注册表单页面,所以他们能够修正错误,然后重新尝试提交。现在,会显示空的表单,但是在下一章中,我们将在表单中显示最初提交的值并将校验错误反馈给用户。

如果没有错误的话,Spitter 对象将会通过 Repository 进行保存,控制器会像之前那样重定向到基本信息页面。

Last updated