本篇介紹如何在Spring Boot使用自訂Java Bean Validation驗證器(custom Java Bean validation)對Controller的請求物件欄位(field)進行格式驗證。
範例環境:
- Windows 64 Bit
- Eclipse
- Java 11
- Spring Boot 2.2.4.RELEASE
把application.properties
設定如下。
application.properties
#context path
server.servlet.context-path=/demo
#port
server.port=8080
所以本機路徑為http://localhost:8080/demo
建立EmailValidator
做為email信箱格式驗證器。
此類別要實作ConstraintValidator<A extends Annotation,T>
。
第一個泛型A
為Annotation
,設為下面的@Email
;
第二個泛形T
為被驗證欄位的型別。由於@Email
是掛在DemoRequest.email
前來驗證信箱格式,所以設為String
。
實作isValid(T value, ConstraintValidatorContext context)
做為實際的驗證邏輯。返回true代表通過,false代表未通過驗證。
EmailValidator
package com.abc.demo.validation.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.abc.demo.validation.annotation.Email;
public class EmailValidator implements ConstraintValidator<Email, String> {
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
// Email格式正則表示式
String regex = "^\\w{1,63}@[a-zA-Z0-9]{2,63}\\.[a-zA-Z]{2,63}(\\.[a-zA-Z]{2,63})?$";
boolean isValid = Pattern.compile(regex).matcher(email).find();
return isValid;
}
}
建立@Email
annotation注釋。用來掛在請求參數欄位前則該欄位會被EmailValidator
驗證。
package com.abc.demo.validation.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.abc.demo.validation.validator.EmailValidator;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { EmailValidator.class }) // 用EmailValidator驗證
public @interface Email {
String message() default "信箱格式錯誤"; // 預設驗證錯誤訊息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
建立PasswordValidator
做為password密碼格式驗證器。
PasswordValidator
package com.abc.demo.validation.validator;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.abc.demo.validation.annotation.Password;
public class PasswordValidator implements ConstraintValidator<Password, String>{
@Override
public boolean isValid(String password, ConstraintValidatorContext context) {
// 至少一個英文,一個數字,長度至少為8個字
String regex = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$";
boolean isValid = Pattern.compile(regex).matcher(password).find();
return isValid;
}
}
建立@Password
annotation注釋。用來掛在請求參數欄位前則該欄位會被PasswordValidator
驗證。
@Password
package com.abc.demo.validation.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import com.abc.demo.validation.validator.PasswordValidator;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { PasswordValidator.class })
public @interface Password {
String message() default "密碼格式錯誤"; // 預設驗證錯誤訊息
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
建立DemoRequest
為Request請求參數物件。在email
前加上@Email
;在password
前加上@Password
。
DemoRequest
package com.abc.demo.controller.request;
import com.abc.demo.validation.annotation.Email;
import com.abc.demo.validation.annotation.Password;
public class DemoRequest {
@Email // 驗證email格式
private String email;
@Password // 驗證password格式
private String password;
// getters and setters...
}
建立DemoController
用來接收Request請求。要被驗證的請求參數前必須加上@Valid
才會進行驗證。
DemoController
package com.abc.demo.controller;
import javax.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.abc.demo.controller.request.DemoRequest;
@RestController
public class DemoController {
@PostMapping(value = "/send")
public void send (@Valid @RequestBody DemoRequest request) {
System.out.println("pass"); // 如果通過自訂的欄位驗證器才會印出
}
}
完成以上後啟動專案,使用Postman送出下面符合驗證格式的請求。
POST | http://localhost:8080/demo/send
Body:raw:JSON
{
"email": "test@abc.com",
"password": "abcd1234"
}
送出的信箱及密碼格式皆符合驗證規則,所以會通過驗證並在console印出"pass"。
改用Postman送出不符合驗證格式的請求。
POST | http://localhost:8080/demo/send
Body:raw:JSON
{
"email": "test@abc",
"password": "1234"
}
由於兩個參數格式皆不符無法通過驗證,返回結果如下。
{
"timestamp": "2020-01-29T12:37:54.445+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Password.demoRequest.password",
"Password.password",
"Password.java.lang.String",
"Password"
],
"arguments": [
{
"codes": [
"demoRequest.password",
"password"
],
"arguments": null,
"defaultMessage": "password",
"code": "password"
}
],
"defaultMessage": "密碼格式錯誤",
"objectName": "demoRequest",
"field": "password",
"rejectedValue": "1234",
"bindingFailure": false,
"code": "Password"
},
{
"codes": [
"Email.demoRequest.email",
"Email.email",
"Email.java.lang.String",
"Email"
],
"arguments": [
{
"codes": [
"demoRequest.email",
"email"
],
"arguments": null,
"defaultMessage": "email",
"code": "email"
}
],
"defaultMessage": "信箱格式錯誤",
"objectName": "demoRequest",
"field": "email",
"rejectedValue": "test@abc",
"bindingFailure": false,
"code": "Email"
}
],
"message": "Validation failed for object='demoRequest'. Error count: 2",
"path": "/demo/send"
}
專案src/main
的目錄結構如下。
demo/src/main
├─java
│ └─com
│ └─abc
│ └─demo
│ │ DemoApplication.java
│ │
│ ├─controller
│ │ │ DemoController.java
│ │ │
│ │ └─request
│ │ DemoRequest.java
│ │
│ └─validation
│ ├─annotation
│ │ Email.java
│ │ Password.java
│ │
│ └─validator
│ EmailValidator.java
│ PasswordValidator.java
│
└─resources
│ application.properties
│
├─static
└─templates
參考:
沒有留言:
張貼留言