文档

§处理表单提交

§启用/禁用表单模块

默认情况下,Play在启用PlayJava sbt插件时包含Java表单模块(play-java-forms),因此如果您已经在项目中启用了enablePlugins(PlayJava),则无需启用任何内容。

表单模块也以javaForms的形式在PlayImport中可用,可以在您的build.sbt中使用libraryDependencies += javaForms

注意:如果您没有使用表单,则可以使用PlayMinimalJava sbt插件而不是PlayJava来删除表单依赖项。这还允许您删除表单模块仅使用的几个传递依赖项,包括几个Spring模块和Hibernate验证器。

§定义表单

play.data包包含几个助手来处理HTTP表单数据提交和验证。处理表单提交的最简单方法是定义一个包装现有类的play.data.Form

import play.libs.Files.TemporaryFile;
import play.mvc.Http.MultipartFormData.FilePart;

public class User {

  protected String email;
  protected String password;
  protected FilePart<TemporaryFile> profilePicture;

  public void setEmail(String email) {
    this.email = email;
  }

  public String getEmail() {
    return email;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getPassword() {
    return password;
  }

  public FilePart<TemporaryFile> getProfilePicture() {
    return profilePicture;
  }

  public void setProfilePicture(FilePart<TemporaryFile> pic) {
    this.profilePicture = pic;
  }
}

上面的表单定义了emailpassword文本字段以及profilePicture文件输入字段,这意味着相应的HTML表单必须使用multipart/form-data编码才能上传文件。
如您所见,默认情况下,您必须定义getter和setter方法,以便Play能够访问表单字段。但是,您也可以通过在conf/application.conf中设置play.forms.binding.directFieldAccess = true来启用“直接字段访问”(对于所有表单)。在这种模式下,Play将忽略getter和setter方法,并尝试直接访问字段

import play.libs.Files.TemporaryFile;
import play.mvc.Http.MultipartFormData.FilePart;

public class User {

  public String email;
  public String password;
  public FilePart<TemporaryFile> profilePicture;
}

注意: 当使用“直接字段访问”且 Play 在表单绑定期间无法访问字段(例如,如果字段或包含该字段的类未定义为public),Play 将尝试通过调用field.setAccessible(true)来通过反射使字段可访问。根据 Java 版本(8+)、JVM 和安全管理器设置,这可能会导致有关非法反射访问的警告,或者在最坏的情况下,抛出SecurityException

要包装一个类,您必须将play.data.FormFactory注入您的控制器,然后才能创建表单

Form<User> userForm = formFactory.form(User.class);

您可以只为特定表单启用“直接字段访问”,而不是为所有表单启用它

Form<User> userForm = formFactory.form(User.class).withDirectFieldAccess(true);

注意: 底层绑定是使用Spring 数据绑定器完成的。

此表单可以从HashMap<String,String>(用于文本数据)和Map<String, FilePart<?>>(用于文件数据)生成User结果值

Map<String, String> textData = new HashMap<>();
textData.put("email", "[email protected]");
textData.put("password", "secret");

Map<String, FilePart<?>> files = new HashMap<>();
files.put("profilePicture", myProfilePicture);

User user = userForm.bind(lang, attrs, textData, files).get();

如果您在作用域中有一个请求,您可以直接从请求内容进行绑定

User user = userForm.bindFromRequest(request).get();

§定义约束

您可以使用JSR-380(Bean Validation 2.0)注释定义在绑定阶段将检查的其他约束

public class User {

  @Required protected String email;
  protected String password;

  public void setEmail(String email) {
    this.email = email;
  }

  public String getEmail() {
    return email;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getPassword() {
    return password;
  }
}

提示: play.data.validation.Constraints类包含多个内置验证注释。所有这些约束注释都定义为@Repeatable。这使您可以例如在同一个元素上多次重复使用相同的注释,但每次使用不同的groups。但是,对于某些约束,让它们重复本身是有意义的,例如@ValidateWith/@ValidatePayloadWith

在下面进一步的高级验证部分,您将学习如何处理跨字段验证、部分表单验证或如何在验证期间使用注入的组件(例如,访问数据库)。

§处理绑定失败

当然,如果您能够定义约束,那么您需要能够处理绑定错误。

if (userForm.hasErrors()) {
  return badRequest(views.html.form.render(userForm));
} else {
  User user = userForm.get();
  return ok("Got user " + user);
}

通常,如上所示,表单只是传递给模板。全局错误可以通过以下方式呈现

@if(form.hasGlobalErrors) {
    <p class="error">
        @for(error <- form.globalErrors) {
            <p>@error.format(messages)</p>
        }
    </p>
}

可以使用error.format以以下方式呈现特定字段的错误

@for(error <- form("email").errors) {
    <p>@error.format(messages)</p>
}

请注意,error.format 接受 messages() 作为参数 - 这是一个在 play.18n.Messages 中定义的 JavaI18N 实例。

§使用初始默认值填充表单

有时您需要使用现有值填充表单,通常用于编辑。

userForm = userForm.fill(new User("[email protected]", "secret"));

提示:Form 对象是不可变的 - 对 bind()fill() 等方法的调用将返回一个包含新数据的新的对象。

§处理具有动态字段的表单

如果您需要从具有动态字段的 html 表单中检索数据,可以使用 DynamicForm

public Result hello(Http.Request request) {
  DynamicForm requestData = formFactory.form().bindFromRequest(request);
  String firstname = requestData.get("firstname");
  String lastname = requestData.get("lastname");
  return ok("Hello " + firstname + " " + lastname);
}

§注册自定义 DataBinder

如果您想定义从自定义对象到表单字段字符串的映射,反之亦然,您需要为该对象注册一个新的 Formatter。
您可以通过为 Formatters 注册一个提供程序来实现这一点,该提供程序将执行正确的初始化。
对于像 JavaTime 的 LocalTime 这样的对象,它可能看起来像这样

import java.text.ParseException;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import play.data.format.Formatters;
import play.data.format.Formatters.SimpleFormatter;
import play.i18n.MessagesApi;

@Singleton
public class FormattersProvider implements Provider<Formatters> {

  private final MessagesApi messagesApi;

  @Inject
  public FormattersProvider(MessagesApi messagesApi) {
    this.messagesApi = messagesApi;
  }

  @Override
  public Formatters get() {
    Formatters formatters = new Formatters(messagesApi);

    formatters.register(
        LocalTime.class,
        new SimpleFormatter<LocalTime>() {

          private Pattern timePattern = Pattern.compile("([012]?\\d)(?:[\\s:\\._\\-]+([0-5]\\d))?");

          @Override
          public LocalTime parse(String input, Locale l) throws ParseException {
            Matcher m = timePattern.matcher(input);
            if (!m.find()) throw new ParseException("No valid Input", 0);
            int hour = Integer.valueOf(m.group(1));
            int min = m.group(2) == null ? 0 : Integer.valueOf(m.group(2));
            return LocalTime.of(hour, min);
          }

          @Override
          public String print(LocalTime localTime, Locale l) {
            return localTime.format(DateTimeFormatter.ofPattern("HH:mm"));
          }
        });

    return formatters;
  }
}

定义提供程序后,您必须绑定它

import com.google.inject.AbstractModule;
import play.data.format.Formatters;

public class FormattersModule extends AbstractModule {

  @Override
  protected void configure() {

    bind(Formatters.class).toProvider(FormattersProvider.class);
  }
}

最后,您必须在 application.conf 中禁用 Play 的默认 FormattersModule,并改为启用您的模块

play.modules.enabled += "com.example.FormattersModule"
play.modules.disabled += "play.data.format.FormattersModule"

当绑定失败时,会创建一个错误键数组,消息文件中定义的第一个键将被使用。此数组通常包含

["error.invalid.<fieldName>", "error.invalid.<type>", "error.invalid"]

错误键由 Spring DefaultMessageCodesResolver 创建,根“typeMismatch”被替换为“error.invalid”。

§高级验证

Play 的内置验证模块在幕后使用 Hibernate Validator。这意味着我们可以利用在 JSR-380 (Bean Validation 2.0) 中定义的功能。Hibernate Validator 文档可以在这里找到 here

§跨字段验证

为了验证整个对象的 state,我们可以使用 类级约束
为了让您免于实现自己的类级约束的负担,Play 开箱即用地提供了这种约束的通用实现,它应该至少涵盖最常见的用例。

现在让我们看看它是如何工作的:要定义一个临时验证,您需要做的就是使用 Play 提供的类级约束 (@Validate) 对您的表单类进行注释,并实现相应的接口(在本例中为 Validatable<String>) - 这会强制您覆盖 validate 方法

import play.data.validation.Constraints;
import play.data.validation.Constraints.Validate;
import play.data.validation.Constraints.Validatable;

@Validate
public class User implements Validatable<String> {

  @Constraints.Required protected String email;
  protected String password;

  @Override
  public String validate() {
    if (authenticate(email, password) == null) {
      // You could also return a key defined in conf/messages
      return "Invalid email or password";
    }
    return null;
  }

  // getters and setters

上面示例中返回的消息将成为全局错误。错误被定义为 play.data.validation.ValidationError
还要注意,在这个例子中,validate 方法和 @Constraints.Required 约束会同时被调用 - 所以无论 @Constraints.Required 是否成功,validate 方法都会被调用(反之亦然)。你将在后面学习如何引入顺序。

如你所见,Validatable<T> 接口接受一个类型参数,该参数决定 validate() 方法的返回值类型。
因此,根据你是否希望通过 validate() 方法向表单添加单个全局错误、一个错误(也可能是全局错误)或多个(可能是全局)错误,你必须使用 StringValidationErrorList<ValidationError> 作为类型参数。Play 会忽略 validate 方法的任何其他返回值类型。

如果验证在 validate() 方法内部通过,你必须返回 null 或一个空的 List。返回任何其他非 null 值(包括空字符串)都被视为验证失败。

当你对特定字段有额外的验证时,返回 ValidationError 对象可能会有用。

import play.data.validation.Constraints.Validate;
import play.data.validation.Constraints.Validatable;
import play.data.validation.ValidationError;

@Validate
public static class LoginForm implements Validatable<ValidationError> {

  // fields, getters, setters, etc.

  @Override
  public ValidationError validate() {
    if (authenticate(email, password) == null) {
      // Error will be displayed for the email field:
      return new ValidationError("email", "Invalid credentials");
    }
    return null;
  }
}

你可以通过返回 List<ValidationError> 来添加多个验证错误。这可以用来为特定字段添加验证错误、全局错误,甚至混合使用这些选项。

import play.data.validation.Constraints.Validate;
import play.data.validation.Constraints.Validatable;
import play.data.validation.ValidationError;
import java.util.List;
import java.util.ArrayList;

@Validate
public static class SignUpForm implements Validatable<List<ValidationError>> {

  // fields, getters, setters, etc.

  @Override
  public List<ValidationError> validate() {
    final List<ValidationError> errors = new ArrayList<>();
    if (authenticate(email, password) == null) {
      // Add an error which will be displayed for the email field:
      errors.add(new ValidationError("email", "Access denied"));
      // Also add a global error:
      errors.add(new ValidationError("", "Form could not be submitted"));
    }
    return errors;
  }
}

如你所见,当使用空字符串作为 ValidationError 的键时,它会变成一个全局错误。

还有一点:你可以使用在 conf/messages 中定义的消息键,而不是写出错误消息,并向它们传递参数。当在模板中显示验证错误时,消息键及其参数将由 Play 自动解析。

// Global error without internationalization:
new ValidationError("", "Errors occurred. Please check your input!");
// Global error; "validationFailed" should be defined in `conf/messages` - taking two arguments:
new ValidationError("", "validationFailed", Arrays.asList(arg1, arg2));
// Error for the email field; "emailUsedAlready" should be defined in `conf/messages` - taking
// the email as argument:
new ValidationError("email", "emailUsedAlready", Arrays.asList(email));

§通过组进行部分表单验证

当用户提交表单时,可能存在你不想一次性验证所有约束,而只想验证其中一些的情况。例如,考虑一个 UI 向导,在每个步骤中,只有指定的约束子集应该被验证。

或者考虑一个 Web 应用程序的注册和登录流程。通常,对于这两个流程,你都希望用户输入电子邮件地址和密码。因此,这些流程将需要几乎相同的表单,除了注册流程,用户还需要输入密码确认。为了使事情更有趣,让我们假设用户也可以在已经登录的情况下,在设置页面上更改他的用户数据 - 这将需要第三个表单。

在这种情况下使用三个不同的表单并不是一个好主意,因为你无论如何都会对大多数表单字段使用相同的约束注释。如果你为 name 字段定义了 255 的最大长度约束,然后想将其更改为仅 100 的限制,该怎么办?你必须为每个表单更改它。可以想象,如果你忘记更新其中一个表单,这将很容易出错。

幸运的是,我们可以简单地 分组约束

import javax.validation.groups.Default;
import play.data.validation.Constraints;
import play.data.validation.Constraints.Validatable;
import play.data.validation.Constraints.Validate;
import play.data.validation.ValidationError;

@Validate(groups = {SignUpCheck.class})
public class PartialUserForm implements Validatable<ValidationError> {

  @Constraints.Required(groups = {Default.class, SignUpCheck.class, LoginCheck.class})
  @Constraints.Email(groups = {Default.class, SignUpCheck.class})
  private String email;

  @Constraints.Required private String firstName;

  @Constraints.Required private String lastName;

  @Constraints.Required(groups = {SignUpCheck.class, LoginCheck.class})
  private String password;

  @Constraints.Required(groups = {SignUpCheck.class})
  private String repeatPassword;

  @Override
  public ValidationError validate() {
    if (!checkPasswords(password, repeatPassword)) {
      return new ValidationError("repeatPassword", "Passwords do not match");
    }
    return null;
  }

  // getters and setters

SignUpCheckLoginCheck 组被定义为两个接口。

public interface SignUpCheck {}
public interface LoginCheck {}

对于注册流程,我们只需将 SignUpCheck 组传递给 form(...) 方法。

Form<PartialUserForm> form =
    formFactory().form(PartialUserForm.class, SignUpCheck.class).bindFromRequest(request);

在这种情况下,电子邮件地址是必需的,并且必须是有效的电子邮件地址,密码和密码确认都是必需的,并且两个密码必须相等(因为@Validate注释调用了validate方法)。但是我们不关心名字和姓氏 - 它们可以为空,或者我们甚至可以从注册页面中排除这些输入字段。

对于登录过程,我们只传递LoginCheck组。

Form<PartialUserForm> form =
    formFactory().form(PartialUserForm.class, LoginCheck.class).bindFromRequest(request);

现在我们只需要输入电子邮件地址和密码 - 仅此而已。我们甚至不关心电子邮件是否有效。你可能不会向用户显示任何其他表单字段,因为我们无论如何都不会验证它们。

想象一下,我们还有一个页面,用户可以在其中更改用户数据(但不能更改密码)。

Form<PartialUserForm> form =
    formFactory().form(PartialUserForm.class, Default.class).bindFromRequest(request);

这与以下内容完全相同。

Form<PartialUserForm> form =
    formFactory().form(PartialUserForm.class).bindFromRequest(request);

在这种情况下,将验证以下约束:电子邮件地址是必需的,并且必须有效,并且名字和姓氏也是必需的 - 这是因为如果约束注释没有明确定义group,则将使用Default组。
请注意,我们没有检查任何密码约束:因为它们明确定义了group属性,但没有包含Default组,因此它们在这里不会被考虑。

如最后一个示例所示,当传递组javax.validation.groups.Default时,你可以省略它 - 因为它本来就是默认的。
但是,一旦你传递了任何其他组,如果你希望在验证过程中考虑其任何字段,你也必须明确传递Default组。

提示:你可以向form(...)方法传递任意多个组(不仅仅是一个)。为了明确起见:这些组将同时被验证 - 而不是一个接一个地验证。

对于高级用法,一组约束可以包含另一个组。你可以使用组继承来实现这一点。

§定义约束组的顺序

你可以按序列验证组。这意味着组将一个接一个地验证 - 但只有在前面的组成功验证后才会验证下一个组。(但是,现在无法确定组内约束验证的顺序 - 这将是Bean Validation 未来版本的一部分)。

基于上面的例子,让我们定义一个组序列

import javax.validation.GroupSequence;
import javax.validation.groups.Default;

@GroupSequence({Default.class, SignUpCheck.class, LoginCheck.class})
public interface OrderedChecks {}

现在我们可以使用它了

Form<PartialUserForm> form =
    formFactory().form(PartialUserForm.class, OrderedChecks.class).bindFromRequest(request);

使用这个组序列将首先验证属于Default组的所有字段(同样也包括没有定义组的字段)。只有当属于Default组的所有字段验证成功后,才会验证属于SignUpCheck组的字段,以此类推。

使用组序列尤其是在你有一个validate方法需要查询数据库或执行其他阻塞操作时是一个好习惯:如果验证在基本级别失败(电子邮件无效、数字是字符串等),执行该方法就没有意义了。在这种情况下,你可能希望validate方法只在检查所有其他基于注解的约束之前,并且只有在它们通过的情况下才被调用。例如,一个注册的用户应该输入一个有效的电子邮件地址,并且只有在它有效的情况下,才应该在之后进行数据库查找以检查电子邮件地址是否存在。

§将有效载荷传递给验证器

如果需要,你也可以将一个ValidationPayload对象(它包含有时在验证过程中需要的有用信息)传递给validate方法。
要传递这样的有效载荷,只需用@ValidateWithPayload(而不是@Validate)注解你的表单,并实现ValidatableWithPayload(而不是Validatable

import java.util.Map;

import com.typesafe.config.Config;

import play.data.validation.Constraints.ValidatableWithPayload;
import play.data.validation.Constraints.ValidateWithPayload;
import play.data.validation.ValidationError;
import play.data.validation.ValidationPayload;

import play.i18n.Lang;
import play.i18n.Messages;

@ValidateWithPayload
public class ChangePasswordForm implements ValidatableWithPayload<ValidationError>
public static class ChangePasswordForm implements ValidatableWithPayload<ValidationError> {

  // fields, getters, setters, etc.

  @Override
  public ValidationError validate(ValidationPayload payload) {
    Lang lang = payload.getLang();
    Messages messages = payload.getMessages();
    TypedMap attrs = payload.getAttrs();
    Config config = payload.getConfig();
    // ...
  }
}

§支持 DI 的自定义类级别约束

有时你需要更复杂的验证过程。例如,当用户注册时,你想检查他的电子邮件地址是否已存在于数据库中,如果存在,则验证应该失败。

因为约束支持运行时依赖注入,我们可以轻松地创建自己的自定义(类级别)约束,它可以注入一个Database对象,我们可以在以后的验证过程中使用它。当然,你也可以注入其他组件,如MessagesApiJPAApi等。

注意: 每个跨关注点只需要创建一个类级别约束。例如,我们将在本节中创建的约束是可重用的,并且可以用于所有需要访问数据库的验证过程。Play 不提供任何带有依赖注入组件的通用类级别约束的原因是,Play 不知道您的项目中可能启用了哪些组件。

首先,让我们设置一个接口,其中包含我们将在表单中实现的 validate 方法。您可以看到该方法传递了一个 Database 对象(查看 数据库文档)。

无负载
import play.db.Database;

public interface ValidatableWithDB<T> {
  public T validate(final Database db);
}
有负载
import play.data.validation.Constraints.ValidationPayload;
import play.db.Database;

public interface ValidatableWithDB<T> {
  public T validate(final Database db, final ValidationPayload payload);
}

我们还需要在表单类上添加的类级别注释

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Repeatable(ValidateWithDB.List.class)
@Constraint(validatedBy = ValidateWithDBValidator.class)
public @interface ValidateWithDB {
  String message() default "error.invalid";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

  /** Defines several {@code @ValidateWithDB} annotations on the same element. */
  @Target({TYPE, ANNOTATION_TYPE})
  @Retention(RUNTIME)
  public @interface List {
    ValidateWithDB[] value();
  }
}

最后,这是我们的约束实现的样子

无负载
import javax.inject.Inject;
import javax.validation.ConstraintValidatorContext;
import play.data.validation.Constraints.PlayConstraintValidator;
import play.db.Database;

public class ValidateWithDBValidator
    implements PlayConstraintValidator<ValidateWithDB, ValidatableWithDB<?>> {

  private final Database db;

  @Inject
  public ValidateWithDBValidator(final Database db) {
    this.db = db;
  }

  @Override
  public void initialize(final ValidateWithDB constraintAnnotation) {}

  @Override
  public boolean isValid(
      final ValidatableWithDB<?> value,
      final ConstraintValidatorContext constraintValidatorContext) {
    return reportValidationStatus(value.validate(this.db), constraintValidatorContext);
  }
}
有负载
import javax.inject.Inject;
import javax.validation.ConstraintValidatorContext;
import play.data.validation.Constraints.PlayConstraintValidatorWithPayload;
import play.data.validation.Constraints.ValidationPayload;
import play.db.Database;

public class ValidateWithDBValidator
    implements PlayConstraintValidatorWithPayload<ValidateWithDB, ValidatableWithDB<?>> {

  private final Database db;

  @Inject
  public ValidateWithDBValidator(final Database db) {
    this.db = db;
  }

  @Override
  public void initialize(final ValidateWithDB constraintAnnotation) {}

  @Override
  public boolean isValid(
      final ValidatableWithDB<?> value,
      final ValidationPayload payload,
      final ConstraintValidatorContext constraintValidatorContext) {
    return reportValidationStatus(value.validate(this.db, payload), constraintValidatorContext);
  }
}

注意: 不要将 ValidationPayloadConstraintValidatorContext 混淆:前者由 Play 提供,是您在处理 Play 中的表单时日常工作中使用的。后者由 Bean Validation 规范 定义,仅在 Play 内部使用 - 只有一个例外:当您编写自己的自定义类级别约束时,此类会出现,您只需要将其传递给 reportValidationStatus 方法,无论如何。

如您所见,我们将 Database 对象注入到约束的构造函数中,并在稍后调用 validate 时使用它。使用运行时依赖注入时,Guice 会自动注入 Database 对象,但对于编译时依赖注入,您需要自己完成。

import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.data.FormFactoryComponents;
import play.data.validation.MappedConstraintValidatorFactory;
import play.db.DBComponents;
import play.db.HikariCPComponents;
import play.filters.components.NoHttpFiltersComponents;
import play.routing.Router;

public class ValidateWithDBComponents extends BuiltInComponentsFromContext
    implements FormFactoryComponents, DBComponents, HikariCPComponents, NoHttpFiltersComponents {

  public ValidateWithDBComponents(ApplicationLoader.Context context) {
    super(context);
  }

  @Override
  public Router router() {
    return Router.empty();
  }

  @Override
  public MappedConstraintValidatorFactory constraintValidatorFactory() {
    return new MappedConstraintValidatorFactory()
        .addConstraintValidator(
            ValidateWithDBValidator.class, new ValidateWithDBValidator(database("default")));
  }
}

注意:您不需要自己创建 database 实例,它已经在实现的接口中定义了。

这样,您的验证器将在需要时可用。

在编写自己的类级别约束时,您可以将以下对象传递给 reportValidationStatus 方法:ValidationErrorList<ValidationError>String(作为全局错误处理)。Play 会忽略任何其他对象。

最后,我们可以使用自定义类级别约束来验证表单

无负载
import play.data.validation.Constraints;
import play.data.validation.ValidationError;
import play.db.Database;

@ValidateWithDB
public class DBAccessForm implements ValidatableWithDB<ValidationError> {

  @Constraints.Required @Constraints.Email private String email;

  @Constraints.Required private String firstName;

  @Constraints.Required private String lastName;

  @Constraints.Required private String password;

  @Constraints.Required private String repeatPassword;

  @Override
  public ValidationError validate(final Database db) {
    // Access the database to check if the email already exists
    if (User.byEmail(email, db) != null) {
      return new ValidationError("email", "This e-mail is already registered.");
    }
    return null;
  }

  // getters and setters
有负载
import play.data.validation.Constraints;
import play.data.validation.Constraints.ValidationPayload;
import play.data.validation.ValidationError;
import play.db.Database;

@ValidateWithDB
public class DBAccessForm implements ValidatableWithDB<ValidationError> {

  @Constraints.Required @Constraints.Email private String email;

  @Constraints.Required private String firstName;

  @Constraints.Required private String lastName;

  @Constraints.Required private String password;

  @Constraints.Required private String repeatPassword;

  @Override
  public ValidationError validate(final Database db, final ValidationPayload payload) {
    // Access the database to check if the email already exists
    if (User.byEmail(email, db) != null) {
      return new ValidationError("email", "This e-mail is already registered.");
    }
    return null;
  }

  // getters and setters

提示:您可能已经注意到,您甚至可以实现多个接口,从而在您的表单类上添加多个类级别约束注释。通过验证组,您就可以只调用所需的验证方法(甚至在一次验证过程中调用多个方法)。

下一步:防止 CSRF 攻击


发现此文档中的错误?此页面的源代码可以在 这里 找到。在阅读 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?前往 我们的社区论坛 与社区交流。