반응형

기본 예제

Dependency

  • maven
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
  • gradle
dependencies {
    ...
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Response

@Builder
@Getter
public class Response<T> {
    private Status status;
    private T body;

    public enum Status {
        SUCCESS,
        NOT_VALID_REQUEST
    }
}

ExceptionControllerAdvice

@RestControllerAdvice
public class ExceptionControllerAdvice {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Response<List<String>> notValidException(MethodArgumentNotValidException exception) {
        return Response.<List<String>>builder()
            .status(Response.Status.NOT_VALID_REQUEST)
            .body(exception.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(fieldError -> fieldError.getField() + " : " + fieldError.getDefaultMessage())
                .collect(Collectors.toList()))
            .build();
    }
}

SaveTodoParams

@Data
public class SaveTodoParams {
    @NotEmpty
    private String todoId;
    @NotEmpty
    private String title;
    @Valid // 하위 객체도 유효성 체크가 필요할 경우 달아줘야함
    @NotEmpty
    private List<TodoItem> todoItems;

    @Data
    public static class TodoItem {
        @NotEmpty
        private String itemId;
        @NotEmpty
        private String contents;
        @NotNull
        @Min(1)
        private Integer order;
    }
}

TodoController

@Slf4j
@RestController
public class TodoController {
    @PostMapping("/api/todos")
    public void saveTodos(@Valid @RequestBody SaveTodoParams params) { // 유효성 체크를 위해 @Valid 추가 필요
        log.info("params : {}", params);
    }
}

요청

curl --location --request POST 'http://localhost:8080/api/todos' \
--header 'Content-Type: application/json' \
--data-raw '{
    "todoId": null,
    "title": null,
    "todoItems": [
        {
            "itemId": null,
            "contents": null,
            "order": 0
        }
    ]
}'

응답

{
    "status": "NOT_VALID_REQUEST",
    "body": [
        "todoItems[0].contents : 비어 있을 수 없습니다",
        "todoId : 비어 있을 수 없습니다",
        "todoItems[0].itemId : 비어 있을 수 없습니다",
        "todoItems[0].order : 1 이상이어야 합니다",
        "title : 비어 있을 수 없습니다"
    ]
}

Custom Validation

DateTimePattern

@Documented
@Constraint(validatedBy = DateTimePatternValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DateTimePattern {
    String value() default "yyyy-MM-dd HH:mm:ss";

    String message() default "유효한 포맷 - {value}";

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

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

DateTimePatternValidator

public class DateTimePatternValidator implements ConstraintValidator<DateTimePattern, String> {
    private DateTimePattern dateTimePattern;

    @Override
    public void initialize(DateTimePattern dateTimePattern) {
        this.dateTimePattern = dateTimePattern;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return true;
        }

        try {
            LocalDateTime.parse(value, DateTimeFormatter.ofPattern(dateTimePattern.value()));
        } catch (DateTimeParseException e) {
            return false;
        }

        return true;
    }
}

DemoParams

@Data
public class DemoParams {
    @NotEmpty
    @DateTimePattern("yyyy.MM.dd HH.mm")
    private String date;
}

DemoController

@Slf4j
@RestController
public class DemoController {
    @PostMapping("/api/demo")
    public void demo(@Valid @RequestBody DemoParams params) {
        log.info("params : {}", params);
    }
}

요청

curl --location --request POST 'http://localhost:8080/api/demo' \
--header 'Content-Type: application/json' \
--data-raw '{
    "date": "2021-12-31 23:59:59"
}'

응답

{
    "status": "NOT_VALID_REQUEST",
    "body": [
        "date : 유효한 포맷 - yyyy.MM.dd HH.mm"
    ]
}
반응형

'Development > Spring' 카테고리의 다른 글

[Spring] Argument Resolver  (0) 2020.12.27
[Spring] Filter & Interceptor  (0) 2020.12.27
[Spring] BeanFactory & ApplicationContext  (0) 2020.12.27
[Spring] Paging  (0) 2020.12.27
[Spring] FileDownload  (0) 2020.12.27

+ Recent posts