可以将业务(自定义)验证放在 DTO 中吗?
Is it okay to put business(custom) validate in the DTO?
@Getter
public class PolicyRequestDto {
@Getter
@Builder
@AllArgsConstructor
public static class InsertPolicyRequest {
@NotNull(message = "need typeNo")
private Integer typeNo;
private String typeDescription;
private Long volume;
private Integer retryCnt;
private Integer authTime;
public InsertPolicyRequest insertValidate() {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(typeNo, "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default : throw new IllegalArgumentException("Error");
}
return this;
}
public PolicyEntity toEntity () {
return PolicyEntity.builder()
.typeNo(typeNo)
.volume(volume)
.retryCnt(retryCnt)
.authTime(authTime)
.build();
}
}
}
public PolicyResponseDto.CommonResponse addWifiServicePolicy(@Valid PolicyRequestDto.InsertPolicyRequest insertRequest) {
insertRequest.insertValidate();
PolicyEntity policyEntity = insertRequest.toEntity();
...
return ...
}
如果无法使用@Validated 注释完成该服务所需的验证,是否可以在 DTO 中完成?
现在,验证过程的级别取决于输入参数,例如代码。该服务只想对通过此验证的数据执行业务逻辑。验证代码放在每个方法的顶部很烦人,但我认为这是必要的代码。
在DTO中这样做有什么问题吗?
p.s我想做一个service对象独立于controller,这样即使只分离service也没有问题
您可以创建自定义验证注释。
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {
String message() default "default error message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
这个注解作用于字段或方法级别。验证器必须实现 ConstraintValidator.
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Integer> {
@Override
public boolean isValid(Integer typeNo, ConstraintValidatorContext context) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(typeNo, "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
如果您需要从其他字段访问数据,则需要更新注释以适用于类型级别 - @Target( { ElementType.TYPE })
,并相应地更改验证器以使用类型 - InsertPolicyRequest
:
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, InsertPolicyRequest> {
@Override
public boolean isValid(InsertPolicyRequest request, ConstraintValidatorContext context) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(request.getTypeNo(), "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
也检查这个 guide。
编辑: 只需一个注解即可。有一个接口将负责提取公共数据,我们称之为 PolicyRequestData
.
public interface PolicyRequestData {
Integer getTypeNo();
String getTypeName();
}
然后将上面的注释更改为在类型级别上工作,并将其重命名为 MyCommonConstraint
。更新 ConstraintValidator
以使用 PolicyRequestData
而不是具体的 class.
public class MyCommonConstraintValidator implements ConstraintValidator<MyCommonConstraint, PolicyRequestData> {
@Override
public boolean isValid(PolicyRequestData request, ConstraintValidatorContext constraintValidatorContext) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(request.getTypeNo(), "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
像这样注释将在 any class 实现 PolicyRequestData
.
上工作
@MyCommonConstraint
public class InsertPolicyRequest implements PolicyRequestData {
private Integer typeNo;
private String typeName;
@Override
public Integer getTypeNo() {
return this.typeNo;
}
@Override
public String getTypeName() {
return this.typeName;
}
//setters, if needed
}
UpdatePolicyRequest
完全相同,除了 class 名称。
如果您需要使用特定数据验证 classes,请扩展接口。
public interface SpecificPolicyRequestData extends PolicyRequestData {
String getOtherData();
}
让特定的 class 实现它并创建注释以使用此接口。然后在 class 上应用两个注释(如果需要两个验证):
@MyCommonConstraint
@MySpecificConstraint
public class SomeSpecificPolicyRequest implements SpecificPolicyRequestData {
@Override
public Integer getTypeNo() {
return null;
}
@Override
public String getTypeName() {
return null;
}
@Override
public String getOtherData() {
return null;
}
}
@Getter
public class PolicyRequestDto {
@Getter
@Builder
@AllArgsConstructor
public static class InsertPolicyRequest {
@NotNull(message = "need typeNo")
private Integer typeNo;
private String typeDescription;
private Long volume;
private Integer retryCnt;
private Integer authTime;
public InsertPolicyRequest insertValidate() {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(typeNo, "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default : throw new IllegalArgumentException("Error");
}
return this;
}
public PolicyEntity toEntity () {
return PolicyEntity.builder()
.typeNo(typeNo)
.volume(volume)
.retryCnt(retryCnt)
.authTime(authTime)
.build();
}
}
}
public PolicyResponseDto.CommonResponse addWifiServicePolicy(@Valid PolicyRequestDto.InsertPolicyRequest insertRequest) {
insertRequest.insertValidate();
PolicyEntity policyEntity = insertRequest.toEntity();
...
return ...
}
如果无法使用@Validated 注释完成该服务所需的验证,是否可以在 DTO 中完成?
现在,验证过程的级别取决于输入参数,例如代码。该服务只想对通过此验证的数据执行业务逻辑。验证代码放在每个方法的顶部很烦人,但我认为这是必要的代码。
在DTO中这样做有什么问题吗?
p.s我想做一个service对象独立于controller,这样即使只分离service也没有问题
您可以创建自定义验证注释。
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {
String message() default "default error message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
这个注解作用于字段或方法级别。验证器必须实现 ConstraintValidator.
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Integer> {
@Override
public boolean isValid(Integer typeNo, ConstraintValidatorContext context) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(typeNo, "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
如果您需要从其他字段访问数据,则需要更新注释以适用于类型级别 - @Target( { ElementType.TYPE })
,并相应地更改验证器以使用类型 - InsertPolicyRequest
:
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, InsertPolicyRequest> {
@Override
public boolean isValid(InsertPolicyRequest request, ConstraintValidatorContext context) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(request.getTypeNo(), "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
也检查这个 guide。
编辑: 只需一个注解即可。有一个接口将负责提取公共数据,我们称之为 PolicyRequestData
.
public interface PolicyRequestData {
Integer getTypeNo();
String getTypeName();
}
然后将上面的注释更改为在类型级别上工作,并将其重命名为 MyCommonConstraint
。更新 ConstraintValidator
以使用 PolicyRequestData
而不是具体的 class.
public class MyCommonConstraintValidator implements ConstraintValidator<MyCommonConstraint, PolicyRequestData> {
@Override
public boolean isValid(PolicyRequestData request, ConstraintValidatorContext constraintValidatorContext) {
PolicyTypeEnum policyEnumList = new PolicyTypeEnum();
Stream<Integer> volumeList = policyEnumList.getVolumeList().stream();
Stream<Integer> authTimeList = policyEnumList.getAuthTimeList().stream();
Stream<Integer> retryCntList = policyEnumList.getRetryCntList().stream();
switch (Objects.requireNonNull(request.getTypeNo(), "no typeNo")) {
case 0 :
// case 0
break;
case 1 :
// case 1
break;
case 2 :
// case 2
break;
default :
return false;
}
return true;
}
}
像这样注释将在 any class 实现 PolicyRequestData
.
@MyCommonConstraint
public class InsertPolicyRequest implements PolicyRequestData {
private Integer typeNo;
private String typeName;
@Override
public Integer getTypeNo() {
return this.typeNo;
}
@Override
public String getTypeName() {
return this.typeName;
}
//setters, if needed
}
UpdatePolicyRequest
完全相同,除了 class 名称。
如果您需要使用特定数据验证 classes,请扩展接口。
public interface SpecificPolicyRequestData extends PolicyRequestData {
String getOtherData();
}
让特定的 class 实现它并创建注释以使用此接口。然后在 class 上应用两个注释(如果需要两个验证):
@MyCommonConstraint
@MySpecificConstraint
public class SomeSpecificPolicyRequest implements SpecificPolicyRequestData {
@Override
public Integer getTypeNo() {
return null;
}
@Override
public String getTypeName() {
return null;
}
@Override
public String getOtherData() {
return null;
}
}