§處理表單提交
§啟用/停用表單模組
預設情況下,啟用 PlayJava
sbt 外掛時,Play 會包含 Java 表單模組 (play-java-forms
),因此如果專案中已經有 enablePlugins(PlayJava)
,則無需啟用任何內容。
表單模組在 PlayImport
中也可用作 javaForms
,可以在 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;
}
}
上述表單定義了 email
和 password
文字欄位,以及 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>
產生 User
結果值,以取得文字資料,並從 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
如果您想定義自訂物件與表單欄位字串之間的對應,以及反之亦然,您需要為這個物件註冊一個新的格式化器。
您可以透過註冊 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);
}
}
最後,您必須停用 Play 的預設 FormattersModule
,並在 application.conf
中啟用您的模組
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 文件可以在 這裡 找到。
§跨欄位驗證
若要驗證整個物件的狀態,我們可以使用 類別層級約束。
為了讓您免於實作自己的類別層級約束的負擔,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()
新增單一全域錯誤、一個錯誤 (也可能是全域錯誤) 或多個 (可能是全域) 錯誤到表單中,您必須使用 String
、ValidationError
或 List<ValidationError>
作為類型引數。Play 會忽略驗證方法的任何其他傳回類型。
如果驗證通過 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
SignUpCheck
和 LoginCheck
群組定義為兩個介面
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
物件,我們可以在驗證程序中使用它。當然,您也可以注入其他元件,例如 MessagesApi
、JPAApi
等。
注意:您只需要為每個橫向問題建立一個類別層級約束。例如,我們將在本節建立的約束是可重複使用的,可用於所有需要存取資料庫的驗證程序。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); } }
注意:不要將
ValidationPayload
和ConstraintValidatorContext
混淆:前者類別由 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
方法:ValidationError
、List<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
在此文件檔中發現錯誤?此頁面的原始程式碼可以在 這裡 找到。在閱讀 文件檔指南 後,請隨時提交拉取請求。有問題或建議要分享?請前往 我們的社群論壇 與社群展開討論。