網頁

2020/5/15

Spring Boot Java Bean Validation class-level constraint date range 日期區間驗證

在Spring Boot使用Java Bean validator 類別驗證器驗證傳入物件的日期範圍。

新增一個類別驗證annotation @DateRange,用來掛在要驗證的物件類別上。屬性beginDateFieldNameendDateFieldName用來指定要驗證物件中的起始日期及結束日期欄位名稱。

DateRange

package com.abc.demo.validation.annotation;

import com.abc.demo.validation.validator.DateRangeValidator;

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

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

@Constraint(validatedBy = DateRangeValidator.class)
@Target({ TYPE }) // Class-level constraints
@Retention(RUNTIME)
@Documented
public @interface DateRange {

    String message() default "End date must be equal or after begin date";

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

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

    String beginDateFieldName();
    String endDateFieldName();

}


新增@DateRange依賴的驗證器DateRangeValidator,繼承ConstraintValidator。若起始日期超過結束日期則返回錯誤。

DateRangeValidator

package com.abc.demo.validation.validator;

import com.abc.demo.validation.annotation.DateRange;
import org.apache.commons.beanutils.BeanUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class DateRangeValidator implements ConstraintValidator<DateRange, Object> {

    private String beginDateFieldName;
    private String endDateFieldName;

    @Override
    public void initialize(DateRange constraintAnnotation) {
        beginDateFieldName = constraintAnnotation.beginDateFieldName(); // 從@DateRange取得要驗證的欄位名稱
        endDateFieldName = constraintAnnotation.endDateFieldName();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {     
        if (value == null) { // value為掛有@DateRange的類別,也就是DateRangeRequestDto
            return false;
        }

        try {
            String beginDate = BeanUtils.getProperty(value, beginDateFieldName); // Apache Commons BeanUtils取得指定欄位名稱的值
            String endDate = BeanUtils.getProperty(value, endDateFieldName);

            LocalDate beginLocalDate = LocalDate.parse(beginDate, DateTimeFormatter.ISO_DATE); // yyyy-MM-dd, e.g. 2020-05-10
            LocalDate endLocalDate = LocalDate.parse(endDate, DateTimeFormatter.ISO_DATE);

            return !beginLocalDate.isAfter(endLocalDate); 

        } catch (Exception e) {
            return false;
        }

    }

}

此為API的傳入參數物件,在類別前面掛上@DateRange驗證日期範圍是否正確。使用屬性beginDateFieldNameendDate指定要驗證的起始日期及結束日期欄位名稱。

DateRangeRequestDto

package com.abc.demo.controller.dto;

import com.abc.demo.validation.annotation.DateRange;

@DateRange(beginDateFieldName = "beginDate", endDateFieldName = "endDate")
public class DateRangeRequestDto {

    private String beginDate;
    private String endDate;

    // getters and setters
}

DemoController.createLogByDateRange()接收前端傳來的DateRangeRequestDto,傳入參數前要掛上@Valid才會被驗證。

DemoController

package com.abc.demo.controller;

import com.abc.demo.controller.dto.DateRangeRequestDto;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
public class DemoController {

    @PostMapping(value = "/log")
    public void createLogByDateRange(@Valid @RequestBody DateRangeRequestDto requestDto) {
        String message = String.format("Create log from %s to %s", requestDto.getBeginDate(), requestDto.getEndDate());
        System.out.println(message);
    }
}

使用下面json傳入API POST /log測試。

{
    "beginDate": "2020-05-10",
    "endDate": "2020-05-09"
}

參考github


沒有留言:

張貼留言