iT邦幫忙

2024 iThome 鐵人賽

0
Software Development

從Servlet到Spring MVC系列 第 34

Day34 Spring MVC - Validation

  • 分享至 

  • xImage
  •  

前言

你一定有經驗在網頁的表單輸入錯誤資訊時旁邊出現紅字提示,比如說在輸入身分證字號時輸入不符合規則的身分證字號,又或者輸入了一個不可能存在的生日資訊。出現紅字代表他有做資料驗證,前端驗證可以減輕後端驗證的負擔,後端驗證則是為系統接收資料的最後一道重要關卡,今日我們就來看看Spring MVC如何處理這一塊。

0.創建module

(1) 請參考Day27 module
(2) 使用JSON相關設置請參考Day29

一、Validation

Java最早的JSR-303 Bean Validation就是來做資料驗證的規範,目前Bean Validataion的版本已到3.0,對應目前穩定版本的實作是Hibernate Validator8.0
https://ithelp.ithome.com.tw/upload/images/20241018/20128084f9LiAaauTg.png
reference:https://hibernate.org/validator/releases/

二、Validation使用

我們使用3.0的規範我們從官網知道Jakarta Bean Validation是屬於Java EE的規格而hibernate提供了實作,按照官網的提供的maven資訊引入對應的jar

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>8.0.1.Final</version>
</dependency>

(1) @Valid

透過此註解啟用驗證,就會對設定好的bean進行驗證

(2) BindingResult

會將此驗證的結果封裝到BindingResult物件

Demo

Employee.java

public class Employee {
    @NotNull
    private String empId;
    
    //透過message可以自訂義錯誤返回值
    @NotEmpty(message = "員工姓名不為空")
    private String name;

    @Email
    @NotNull
    private String email;

    @Min(0)
    @Max(110)
    @NotNull
    private int age;

    //getter and setter省略
}

可以透過BindingResult取得驗證不通過的訊息再進行封裝返回前端

@Controller
public class DemoValidateController {

    @ResponseBody
    @PostMapping("SaveEmp")
    public String DemoValidate(@RequestBody @Validated Employee emp, BindingResult result) {
        result.getFieldErrors().forEach(err->{
            System.out.println(err.getField()+":"+err.getDefaultMessage());
        });
        return result.toString();
    }
}

postman test
https://ithelp.ithome.com.tw/upload/images/20241018/20128084OXKePyZmEt.png
backend result
https://ithelp.ithome.com.tw/upload/images/20241018/20128084Tv8InD03mp.png

二、自訂義校驗Custom Validation

以下就試寫一個性別的驗證器

(1) 撰寫Validator

implements ConstraintValidator並覆寫isValid方法

public class GenderValidator implements ConstraintValidator<Gender, String> {
    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return s.equals("男")||s.equals("女");
    }
}

(2) 定義annotation

這邊可以去找一個Validation的annotation來複製改寫

@Documented
//這邊要加入自己寫的Validator
@Constraint(validatedBy = {GenderValidator.class})
//這邊定義此annotation可以放置在那些地方
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Gender {
    String message() default "{性別不對喔}";

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

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

Demo

Employee加入gender屬性

@Gender
private String gender;

postman test
https://ithelp.ithome.com.tw/upload/images/20241018/20128084ypcKSNzURJ.png

backend result
https://ithelp.ithome.com.tw/upload/images/20241018/20128084nxYVHSCQql.png

三、ExceptionHandler

如果在每一個Controller的每一個方法中都需要自己去處理校驗結果BindingResult是不是很累人呢,此時前一天介紹的Exception Handling來解決這個問題。當沒有使用BindingResult去接驗證結果時,驗證失敗會拋出MethodArgumentNotValidException,並且可以從該Exception取出BindingResult,是不是很棒呢

Demo

於DemoValidateController再創建一個方法

@ResponseBody
@PostMapping("SaveEmployee")
public ResponseEntity<String> DemoValidateWithExceptionHandling(@RequestBody @Validated Employee emp) {
    System.out.println("Access SaveEmployee...");
    return ResponseEntity.ok("Save Employee success!!!");
}

創建GlobalExceptionHandler

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Map<String, String> HandleException(MethodArgumentNotValidException ex){
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return errors;
    }
}

https://ithelp.ithome.com.tw/upload/images/20241018/20128084v9Rfl7GbK4.png

Reference


上一篇
Day32 Spring MVC - Exception Handling
下一篇
Day34 Spring MVC - RESTful
系列文
從Servlet到Spring MVC36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言