java - 服务层中的验证(Spring Boot)

标签 java hibernate spring-boot validation design-patterns

我有一个 DTO,它在 Controller 层使用 BeanValidation (javax.validation) 和自定义 validator (org.springframework.validation.Validator) 进行验证。这样我可以检查提供的输入是否有效,然后将 DTO 转换为实体并将其转发到服务层。

    @Data
    public class UserDTO {

            @NotBlank
            @Size(max = 25)
            private String name;

            @NotNull
            private Date birthday;

            @NotNull
            private Date startDate;

            private Date endDate;

            private Long count;

    }

    public class UserDTOValidator implements Validator {
        private static final String START_DATE= "startDate";
        private static final String END_DATE= "endDate";
        private static final String COUNT= "count";

        @Override
        public boolean supports(Class<?> clazz) {
            return UserDTO.class.isAssignableFrom(clazz);
        }
        @Override
        public void validate(Object target, Errors errors) {

            UserDTO vm = (UserDTO) target;

            if (vm.getEndDate() != null) {
               if (vm.getStartDate().after(vm.getEndDate())) {
                errors.rejectValue(START_DATE, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
               }

               if (vm.getEndDate().equals(vm.getStartDate()) || vm.getEndDate().before(vm.getStartDate())) {
                errors.rejectValue(END_DATE, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
               }
            }

            if (vm.getCount() < 1) {
             errors.rejectValue(COUNT, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
            }
            .....

        }

    }


public class UserController {
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new UserDTOValidator());
    }
    @PostMapping()
    public ResponseEntity<UserDTO> create(@RequestBody @Valid UserDTO userDTO) {
       .....
    }
    .....
}

然后是业务逻辑验证。例如:@Entity 用户的 startDate 必须在某个事件发生之后,并且如果最后创建的用户的生日是在夏季,则计数必须大于某个 X,否则,该实体应被用户服务丢弃。
@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private SomeEventService someEventService ;

    @Override
    public User create(User entity) {
        String error = this.validateUser(entity);
        if (StringUtils.isNotBlank(error)) {
            throw new ValidationException(error);
        }

        return this.userRepository.save(entity);
    }
    ....
    private String validateUser(User entity) {
        SomeEvent someEvent = this.someEventService.get(entity.getName()); 
        if (entity.getStartDate().before(someEvent.getDate())) {
            return "startDate";
        }
        User lastUser = this.userRepository.findLast();
        ....
    }

}

但是我觉得这不是处理业务逻辑验证的最佳方法。我该怎么办? ConstraintValidator/HibernateValidator/JPA 事件监听器?他们可以在@Entity 类级别工作,还是我必须为每个不同的字段检查创建 X 个?你们如何在实际的生产应用程序中做到这一点?

最佳答案

在我的建议中,

  • 通过@Valid
  • 使用经典字段级别验证

    sample

    void myservicemethod(@Valid UserDTO user)
    
  • 对于实体级别的自定义业务级别验证,请在 DTO 本身中创建验证方法

  • sample

    class UserDTO {
        //fields and getter setter
        void validate() throws ValidationException {
            //your entity level business logic
        }
    }
    
    此策略将有助于保持实体特定的验证逻辑将保留在实体内
  • 如果您仍然有需要其他服务调用的验证逻辑,则使用自定义 ConstraintValidator 创建自定义验证注释(例如 question on stackoverflow )。在这种情况下,我的偏好是从这个自定义 validator 调用 UserDTO.validate() 来代替从服务调用

  • 这将有助于使您的验证逻辑与服务层分离,并且还具有可移植性和模块化

    关于java - 服务层中的验证(Spring Boot),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56013082/

    相关文章:

    java - 你如何在 Spring Cloud Gateway (Webflux) POST 过滤器中读取响应主体

    java - 有没有办法减少控制台 Java 应用程序的启动时间?

    java - Spring Boot @ConfigurationProperties 从 yaml 加载列表不起作用

    mysql - Hibernate 双向或两个单向关系

    java - 将数组参数绑定(bind)到 native 查询

    java - 如何在 Android 中使用 Proguard 并保护应用程序的功能完整性?

    java - hibernate 中单向 @OneToMany 映射中的子实体计数

    java - 如何使用 Spring Boot 从类中仅返回几种类型?

    java - Json转java

    java - Intellij spring boot 扩展 gradle 构建文件中的 processResources 以替换属性